aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS413
-rw-r--r--NEWS10
-rw-r--r--audio/audiostream.cpp4
-rw-r--r--audio/decoders/aiff.h1
-rw-r--r--audio/decoders/quicktime.cpp8
-rw-r--r--audio/decoders/quicktime.h1
-rw-r--r--audio/decoders/quicktime_intern.h4
-rw-r--r--audio/mididrv.cpp2
-rw-r--r--audio/midiparser.cpp210
-rw-r--r--audio/midiparser.h66
-rw-r--r--audio/midiparser_smf.cpp176
-rw-r--r--audio/midiparser_xmidi.cpp99
-rw-r--r--audio/softsynth/mt32/Part.cpp2
-rw-r--r--audio/softsynth/mt32/Partial.h2
-rw-r--r--audio/softsynth/mt32/PartialManager.cpp2
-rw-r--r--audio/softsynth/mt32/Synth.cpp2
-rw-r--r--audio/softsynth/mt32/TVA.cpp2
-rw-r--r--audio/softsynth/mt32/TVF.cpp4
-rw-r--r--audio/softsynth/mt32/freeverb.cpp2
-rw-r--r--backends/events/openpandora/op-events.cpp33
-rw-r--r--backends/events/sdl/sdl-events.cpp2
-rw-r--r--backends/events/sdl/sdl-events.h2
-rw-r--r--backends/events/webossdl/webossdl-events.h6
-rw-r--r--backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp2
-rw-r--r--backends/graphics/openglsdl/openglsdl-graphics.cpp2
-rw-r--r--backends/midi/coreaudio.cpp31
-rw-r--r--backends/midi/sndio.cpp4
-rw-r--r--backends/mixer/sdl13/sdl13-mixer.cpp4
-rw-r--r--backends/platform/bada/application.cpp2
-rw-r--r--backends/platform/bada/sscanf.cpp2
-rw-r--r--backends/platform/iphone/iphone_video.mm2
-rw-r--r--backends/platform/iphone/osys_video.mm4
-rwxr-xr-xbackends/platform/maemo/debian/rules2
-rw-r--r--backends/platform/openpandora/op-options.cpp5
-rw-r--r--backends/platform/sdl/macosx/macosx.cpp2
-rw-r--r--backends/platform/sdl/macosx/macosx.h2
-rw-r--r--backends/platform/sdl/main.cpp66
-rw-r--r--backends/platform/sdl/module.mk1
-rw-r--r--backends/taskbar/win32/mingw-compat.h4
-rw-r--r--backends/timer/bada/timer.cpp230
-rw-r--r--backends/timer/bada/timer.h124
-rw-r--r--backends/timer/default/default-timer.cpp2
-rw-r--r--base/commandLine.cpp2
-rw-r--r--base/main.cpp3
-rw-r--r--common/array.h2
-rw-r--r--common/coroutines.cpp10
-rw-r--r--common/cosinetables.cpp2
-rw-r--r--common/gui_options.h2
-rw-r--r--common/installshield_cab.cpp (renamed from engines/agos/installshield_cab.cpp)126
-rw-r--r--common/installshield_cab.h (renamed from engines/agos/installshield_cab.h)18
-rw-r--r--common/keyboard.h9
-rw-r--r--common/macresman.cpp22
-rw-r--r--common/macresman.h3
-rw-r--r--common/module.mk1
-rw-r--r--common/quicktime.h2
-rw-r--r--common/rational.h3
-rw-r--r--common/rect.h14
-rw-r--r--common/savefile.h4
-rw-r--r--common/sinetables.cpp2
-rw-r--r--common/zlib.cpp54
-rw-r--r--common/zlib.h21
-rwxr-xr-xconfigure261
-rw-r--r--devtools/create_project/config.h7
-rw-r--r--devtools/create_project/create_project.cpp28
-rw-r--r--devtools/create_project/create_project.h24
-rw-r--r--devtools/create_project/msbuild.cpp21
-rw-r--r--devtools/create_project/msvc.cpp19
-rw-r--r--devtools/create_project/msvc.h2
-rw-r--r--devtools/create_project/scripts/postbuild.cmd4
-rw-r--r--devtools/create_project/visualstudio.cpp8
-rw-r--r--devtools/create_project/xcode.cpp4
-rw-r--r--devtools/create_tony/create_tony.cpp183
-rw-r--r--devtools/create_tony/create_tony.h44
-rw-r--r--devtools/create_tony/module.mk10
-rw-r--r--devtools/create_tony/staticdata.h1370
-rwxr-xr-xdevtools/credits.pl36
-rw-r--r--devtools/module.mk2
-rw-r--r--dists/engine-data/README12
-rw-r--r--dists/engine-data/tony.datbin0 -> 24584 bytes
-rw-r--r--dists/scummvm.rc3
-rw-r--r--dists/win32/ScummVM.iss12
-rw-r--r--engines/advancedDetector.h4
-rw-r--r--engines/agi/agi.cpp2
-rw-r--r--engines/agi/detection_tables.h4
-rw-r--r--engines/agi/loader_v1.cpp16
-rw-r--r--engines/agi/menu.cpp2
-rw-r--r--engines/agi/op_cmd.cpp6
-rw-r--r--engines/agi/op_test.cpp2
-rw-r--r--engines/agi/opcodes.cpp2
-rw-r--r--engines/agi/saveload.cpp2
-rw-r--r--engines/agi/sound_pcjr.cpp4
-rw-r--r--engines/agi/text.cpp4
-rw-r--r--engines/agi/words.cpp4
-rw-r--r--engines/agos/midiparser_s1d.cpp47
-rw-r--r--engines/agos/module.mk1
-rw-r--r--engines/agos/res.cpp8
-rw-r--r--engines/agos/saveload.cpp4
-rw-r--r--engines/agos/sound.cpp2
-rw-r--r--engines/cge/bitmap.cpp4
-rw-r--r--engines/cge/cge_main.cpp14
-rw-r--r--engines/cge/detection.cpp4
-rw-r--r--engines/cge/events.h2
-rw-r--r--engines/cge/fileio.cpp2
-rw-r--r--engines/cge/text.cpp2
-rw-r--r--engines/cine/anim.cpp62
-rw-r--r--engines/cine/cine.cpp13
-rw-r--r--engines/cine/cine.h2
-rw-r--r--engines/cine/console.cpp2
-rw-r--r--engines/cine/gfx.cpp87
-rw-r--r--engines/cine/gfx.h3
-rw-r--r--engines/cine/main_loop.cpp29
-rw-r--r--engines/cine/object.cpp2
-rw-r--r--engines/cine/pal.cpp3
-rw-r--r--engines/cine/part.cpp2
-rw-r--r--engines/cine/saveload.cpp2
-rw-r--r--engines/cine/saveload.h2
-rw-r--r--engines/cine/script.h1
-rw-r--r--engines/cine/script_fw.cpp1640
-rw-r--r--engines/cine/sound.cpp161
-rw-r--r--engines/cine/sound.h36
-rw-r--r--engines/cine/texte.cpp2
-rw-r--r--engines/cine/texte.h2
-rw-r--r--engines/cine/various.cpp218
-rw-r--r--engines/composer/composer.cpp2
-rw-r--r--engines/composer/resource.cpp2
-rw-r--r--engines/configure.engines26
-rw-r--r--engines/cruise/detection.cpp2
-rw-r--r--engines/dialogs.cpp2
-rw-r--r--engines/draci/detection.cpp2
-rw-r--r--engines/drascula/detection.cpp14
-rw-r--r--engines/dreamweb/dreamweb.cpp6
-rw-r--r--engines/dreamweb/dreamweb.h2
-rw-r--r--engines/dreamweb/monitor.cpp4
-rw-r--r--engines/dreamweb/object.cpp4
-rw-r--r--engines/dreamweb/people.cpp16
-rw-r--r--engines/dreamweb/print.cpp2
-rw-r--r--engines/dreamweb/sprite.cpp14
-rw-r--r--engines/dreamweb/vgagrafx.cpp72
-rw-r--r--engines/engines.mk15
-rw-r--r--engines/groovie/detection.cpp2
-rw-r--r--engines/groovie/roq.cpp27
-rw-r--r--engines/hugo/file.cpp82
-rw-r--r--engines/hugo/file.h5
-rw-r--r--engines/lastexpress/data/background.cpp1
-rw-r--r--engines/lastexpress/data/subtitle.cpp6
-rw-r--r--engines/lastexpress/debug.cpp93
-rw-r--r--engines/lastexpress/debug.h3
-rw-r--r--engines/lastexpress/entities/abbot.cpp26
-rw-r--r--engines/lastexpress/entities/abbot.h8
-rw-r--r--engines/lastexpress/entities/august.cpp2
-rw-r--r--engines/lastexpress/entities/chapters.cpp2
-rw-r--r--engines/lastexpress/entities/chapters.h2
-rw-r--r--engines/lastexpress/entities/entity.cpp27
-rw-r--r--engines/lastexpress/entities/entity.h23
-rw-r--r--engines/lastexpress/entities/verges.cpp164
-rw-r--r--engines/lastexpress/entities/verges.h26
-rw-r--r--engines/lastexpress/fight/fight.cpp2
-rw-r--r--engines/lastexpress/game/action.cpp4
-rw-r--r--engines/lastexpress/game/beetle.cpp5
-rw-r--r--engines/lastexpress/game/entities.cpp4
-rw-r--r--engines/lastexpress/game/entities.h2
-rw-r--r--engines/lastexpress/game/inventory.cpp6
-rw-r--r--engines/lastexpress/game/logic.cpp1
-rw-r--r--engines/lastexpress/game/savegame.cpp47
-rw-r--r--engines/lastexpress/game/savegame.h4
-rw-r--r--engines/lastexpress/game/savepoint.cpp4
-rw-r--r--engines/lastexpress/game/scenes.cpp2
-rw-r--r--engines/lastexpress/game/scenes.h2
-rw-r--r--engines/lastexpress/menu/menu.cpp4
-rw-r--r--engines/lastexpress/sound/entry.cpp4
-rw-r--r--engines/lastexpress/sound/queue.cpp6
-rw-r--r--engines/lure/decode.cpp4
-rw-r--r--engines/made/screenfx.cpp8
-rw-r--r--engines/mohawk/bitmap.cpp6
-rw-r--r--engines/mohawk/video.cpp2
-rw-r--r--engines/mohawk/video.h2
-rw-r--r--engines/parallaction/adlib.cpp808
-rw-r--r--engines/parallaction/callables_ns.cpp12
-rw-r--r--engines/parallaction/debug.cpp8
-rw-r--r--engines/parallaction/dialogue.cpp8
-rw-r--r--engines/parallaction/disk_br.cpp4
-rw-r--r--engines/parallaction/disk_ns.cpp2
-rw-r--r--engines/parallaction/exec.cpp6
-rw-r--r--engines/parallaction/exec_br.cpp8
-rw-r--r--engines/parallaction/exec_ns.cpp8
-rw-r--r--engines/parallaction/font.cpp4
-rw-r--r--engines/parallaction/gfxbase.cpp8
-rw-r--r--engines/parallaction/graphics.cpp4
-rw-r--r--engines/parallaction/graphics.h4
-rw-r--r--engines/parallaction/gui_ns.cpp2
-rw-r--r--engines/parallaction/input.cpp22
-rw-r--r--engines/parallaction/module.mk1
-rw-r--r--engines/parallaction/objects.cpp4
-rw-r--r--engines/parallaction/parallaction.cpp32
-rw-r--r--engines/parallaction/parallaction.h24
-rw-r--r--engines/parallaction/parallaction_br.cpp14
-rw-r--r--engines/parallaction/parallaction_ns.cpp14
-rw-r--r--engines/parallaction/parser_br.cpp4
-rw-r--r--engines/parallaction/parser_ns.cpp2
-rw-r--r--engines/parallaction/saveload.cpp6
-rw-r--r--engines/parallaction/sound.h2
-rw-r--r--engines/parallaction/sound_br.cpp34
-rw-r--r--engines/parallaction/sound_ns.cpp6
-rw-r--r--engines/parallaction/staticres.cpp28
-rw-r--r--engines/parallaction/walk.cpp28
-rw-r--r--engines/parallaction/walk.h8
-rw-r--r--engines/pegasus/ai/ai_action.cpp78
-rw-r--r--engines/pegasus/ai/ai_action.h136
-rw-r--r--engines/pegasus/ai/ai_area.cpp613
-rw-r--r--engines/pegasus/ai/ai_area.h172
-rw-r--r--engines/pegasus/ai/ai_condition.cpp290
-rw-r--r--engines/pegasus/ai/ai_condition.h287
-rw-r--r--engines/pegasus/ai/ai_rule.cpp78
-rw-r--r--engines/pegasus/ai/ai_rule.h86
-rw-r--r--engines/pegasus/compass.cpp82
-rw-r--r--engines/pegasus/compass.h58
-rw-r--r--engines/pegasus/console.cpp102
-rw-r--r--engines/pegasus/console.h46
-rw-r--r--engines/pegasus/constants.h729
-rw-r--r--engines/pegasus/cursor.cpp213
-rw-r--r--engines/pegasus/cursor.h84
-rw-r--r--engines/pegasus/detection.cpp157
-rw-r--r--engines/pegasus/elements.cpp568
-rw-r--r--engines/pegasus/elements.h254
-rw-r--r--engines/pegasus/energymonitor.cpp296
-rw-r--r--engines/pegasus/energymonitor.h111
-rw-r--r--engines/pegasus/fader.cpp218
-rw-r--r--engines/pegasus/fader.h130
-rw-r--r--engines/pegasus/gamestate.cpp2359
-rw-r--r--engines/pegasus/gamestate.h886
-rw-r--r--engines/pegasus/graphics.cpp346
-rw-r--r--engines/pegasus/graphics.h95
-rw-r--r--engines/pegasus/hotspot.cpp325
-rw-r--r--engines/pegasus/hotspot.h152
-rw-r--r--engines/pegasus/input.cpp373
-rw-r--r--engines/pegasus/input.h500
-rw-r--r--engines/pegasus/interaction.h110
-rw-r--r--engines/pegasus/interface.cpp667
-rw-r--r--engines/pegasus/interface.h148
-rw-r--r--engines/pegasus/items/autodragger.cpp91
-rw-r--r--engines/pegasus/items/autodragger.h57
-rw-r--r--engines/pegasus/items/biochips/aichip.cpp279
-rw-r--r--engines/pegasus/items/biochips/aichip.h69
-rw-r--r--engines/pegasus/items/biochips/biochipitem.cpp95
-rw-r--r--engines/pegasus/items/biochips/biochipitem.h54
-rw-r--r--engines/pegasus/items/biochips/mapchip.cpp106
-rw-r--r--engines/pegasus/items/biochips/mapchip.h64
-rw-r--r--engines/pegasus/items/biochips/mapimage.cpp443
-rw-r--r--engines/pegasus/items/biochips/mapimage.h84
-rw-r--r--engines/pegasus/items/biochips/opticalchip.cpp190
-rw-r--r--engines/pegasus/items/biochips/opticalchip.h71
-rw-r--r--engines/pegasus/items/biochips/pegasuschip.cpp198
-rw-r--r--engines/pegasus/items/biochips/pegasuschip.h55
-rw-r--r--engines/pegasus/items/biochips/retscanchip.cpp49
-rw-r--r--engines/pegasus/items/biochips/retscanchip.h43
-rw-r--r--engines/pegasus/items/biochips/shieldchip.cpp53
-rw-r--r--engines/pegasus/items/biochips/shieldchip.h46
-rw-r--r--engines/pegasus/items/inventory.cpp175
-rw-r--r--engines/pegasus/items/inventory.h80
-rw-r--r--engines/pegasus/items/inventory/airmask.cpp249
-rw-r--r--engines/pegasus/items/inventory/airmask.h76
-rw-r--r--engines/pegasus/items/inventory/gascanister.cpp46
-rw-r--r--engines/pegasus/items/inventory/gascanister.h44
-rw-r--r--engines/pegasus/items/inventory/inventoryitem.cpp110
-rw-r--r--engines/pegasus/items/inventory/inventoryitem.h67
-rw-r--r--engines/pegasus/items/inventory/keycard.cpp59
-rw-r--r--engines/pegasus/items/inventory/keycard.h48
-rw-r--r--engines/pegasus/items/inventorypicture.cpp370
-rw-r--r--engines/pegasus/items/inventorypicture.h125
-rw-r--r--engines/pegasus/items/item.cpp314
-rw-r--r--engines/pegasus/items/item.h363
-rw-r--r--engines/pegasus/items/itemdragger.cpp193
-rw-r--r--engines/pegasus/items/itemdragger.h96
-rw-r--r--engines/pegasus/items/itemlist.cpp67
-rw-r--r--engines/pegasus/items/itemlist.h59
-rw-r--r--engines/pegasus/menu.cpp1219
-rw-r--r--engines/pegasus/menu.h171
-rw-r--r--engines/pegasus/module.mk100
-rw-r--r--engines/pegasus/movie.cpp280
-rw-r--r--engines/pegasus/movie.h105
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoria.cpp1962
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoria.h523
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp370
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h78
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp1442
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoriabomb.h156
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp115
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoriamessages.h60
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp135
-rw-r--r--engines/pegasus/neighborhood/caldoria/caldoriamirror.h54
-rw-r--r--engines/pegasus/neighborhood/door.cpp64
-rw-r--r--engines/pegasus/neighborhood/door.h90
-rw-r--r--engines/pegasus/neighborhood/exit.cpp70
-rw-r--r--engines/pegasus/neighborhood/exit.h93
-rw-r--r--engines/pegasus/neighborhood/extra.cpp58
-rw-r--r--engines/pegasus/neighborhood/extra.h67
-rw-r--r--engines/pegasus/neighborhood/hotspotinfo.cpp65
-rw-r--r--engines/pegasus/neighborhood/hotspotinfo.h77
-rw-r--r--engines/pegasus/neighborhood/mars/constants.h941
-rw-r--r--engines/pegasus/neighborhood/mars/energybeam.cpp70
-rw-r--r--engines/pegasus/neighborhood/mars/energybeam.h43
-rw-r--r--engines/pegasus/neighborhood/mars/gravitoncannon.cpp134
-rw-r--r--engines/pegasus/neighborhood/mars/gravitoncannon.h57
-rw-r--r--engines/pegasus/neighborhood/mars/hermite.cpp76
-rw-r--r--engines/pegasus/neighborhood/mars/hermite.h41
-rw-r--r--engines/pegasus/neighborhood/mars/mars.cpp3735
-rw-r--r--engines/pegasus/neighborhood/mars/mars.h238
-rw-r--r--engines/pegasus/neighborhood/mars/planetmover.cpp104
-rw-r--r--engines/pegasus/neighborhood/mars/planetmover.h57
-rw-r--r--engines/pegasus/neighborhood/mars/reactor.cpp297
-rw-r--r--engines/pegasus/neighborhood/mars/reactor.h108
-rw-r--r--engines/pegasus/neighborhood/mars/robotship.cpp267
-rw-r--r--engines/pegasus/neighborhood/mars/robotship.h84
-rw-r--r--engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp116
-rw-r--r--engines/pegasus/neighborhood/mars/shuttleenergymeter.h73
-rw-r--r--engines/pegasus/neighborhood/mars/shuttlehud.cpp246
-rw-r--r--engines/pegasus/neighborhood/mars/shuttlehud.h60
-rw-r--r--engines/pegasus/neighborhood/mars/shuttleweapon.cpp129
-rw-r--r--engines/pegasus/neighborhood/mars/shuttleweapon.h68
-rw-r--r--engines/pegasus/neighborhood/mars/spacechase3d.cpp106
-rw-r--r--engines/pegasus/neighborhood/mars/spacechase3d.h91
-rw-r--r--engines/pegasus/neighborhood/mars/spacejunk.cpp212
-rw-r--r--engines/pegasus/neighborhood/mars/spacejunk.h78
-rw-r--r--engines/pegasus/neighborhood/mars/tractorbeam.cpp139
-rw-r--r--engines/pegasus/neighborhood/mars/tractorbeam.h43
-rw-r--r--engines/pegasus/neighborhood/neighborhood.cpp1774
-rw-r--r--engines/pegasus/neighborhood/neighborhood.h408
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp219
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h65
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp445
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/fillingstation.h91
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp763
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/noradalpha.h115
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/panorama.cpp239
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/panorama.h98
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp91
-rw-r--r--engines/pegasus/neighborhood/norad/alpha/panoramascroll.h60
-rw-r--r--engines/pegasus/neighborhood/norad/constants.h755
-rw-r--r--engines/pegasus/neighborhood/norad/delta/globegame.cpp1062
-rw-r--r--engines/pegasus/neighborhood/norad/delta/globegame.h169
-rw-r--r--engines/pegasus/neighborhood/norad/delta/noraddelta.cpp869
-rw-r--r--engines/pegasus/neighborhood/norad/delta/noraddelta.h117
-rw-r--r--engines/pegasus/neighborhood/norad/norad.cpp285
-rw-r--r--engines/pegasus/neighborhood/norad/norad.h121
-rw-r--r--engines/pegasus/neighborhood/norad/noradelevator.cpp130
-rw-r--r--engines/pegasus/neighborhood/norad/noradelevator.h67
-rw-r--r--engines/pegasus/neighborhood/norad/pressuredoor.cpp554
-rw-r--r--engines/pegasus/neighborhood/norad/pressuredoor.h93
-rw-r--r--engines/pegasus/neighborhood/norad/pressuretracker.cpp87
-rw-r--r--engines/pegasus/neighborhood/norad/pressuretracker.h69
-rw-r--r--engines/pegasus/neighborhood/norad/subcontrolroom.cpp1178
-rw-r--r--engines/pegasus/neighborhood/norad/subcontrolroom.h133
-rw-r--r--engines/pegasus/neighborhood/norad/subplatform.cpp205
-rw-r--r--engines/pegasus/neighborhood/norad/subplatform.h63
-rw-r--r--engines/pegasus/neighborhood/prehistoric/prehistoric.cpp689
-rw-r--r--engines/pegasus/neighborhood/prehistoric/prehistoric.h158
-rw-r--r--engines/pegasus/neighborhood/spot.cpp70
-rw-r--r--engines/pegasus/neighborhood/spot.h97
-rw-r--r--engines/pegasus/neighborhood/tsa/fulltsa.cpp3023
-rw-r--r--engines/pegasus/neighborhood/tsa/fulltsa.h159
-rw-r--r--engines/pegasus/neighborhood/tsa/tinytsa.cpp453
-rw-r--r--engines/pegasus/neighborhood/tsa/tinytsa.h71
-rw-r--r--engines/pegasus/neighborhood/turn.cpp63
-rw-r--r--engines/pegasus/neighborhood/turn.h69
-rw-r--r--engines/pegasus/neighborhood/view.cpp60
-rw-r--r--engines/pegasus/neighborhood/view.h68
-rw-r--r--engines/pegasus/neighborhood/wsc/moleculebin.cpp127
-rw-r--r--engines/pegasus/neighborhood/wsc/moleculebin.h72
-rw-r--r--engines/pegasus/neighborhood/wsc/wsc.cpp2542
-rw-r--r--engines/pegasus/neighborhood/wsc/wsc.h166
-rw-r--r--engines/pegasus/neighborhood/zoom.cpp74
-rw-r--r--engines/pegasus/neighborhood/zoom.h70
-rw-r--r--engines/pegasus/notification.cpp149
-rw-r--r--engines/pegasus/notification.h123
-rw-r--r--engines/pegasus/pegasus.cpp2346
-rw-r--r--engines/pegasus/pegasus.h327
-rw-r--r--engines/pegasus/scoring.h281
-rw-r--r--engines/pegasus/sound.cpp181
-rw-r--r--engines/pegasus/sound.h108
-rw-r--r--engines/pegasus/surface.cpp392
-rw-r--r--engines/pegasus/surface.h140
-rw-r--r--engines/pegasus/timers.cpp429
-rw-r--r--engines/pegasus/timers.h260
-rw-r--r--engines/pegasus/transition.cpp200
-rw-r--r--engines/pegasus/transition.h108
-rw-r--r--engines/pegasus/types.h161
-rw-r--r--engines/pegasus/util.cpp77
-rw-r--r--engines/pegasus/util.h117
-rw-r--r--engines/plugins_table.h9
-rw-r--r--engines/queen/display.cpp38
-rw-r--r--engines/queen/queen.cpp4
-rw-r--r--engines/saga/script.cpp2
-rw-r--r--engines/sci/console.cpp6
-rw-r--r--engines/sci/engine/file.cpp4
-rw-r--r--engines/sci/engine/kernel_tables.h2
-rw-r--r--engines/sci/engine/kfile.cpp6
-rw-r--r--engines/sci/engine/kpathing.cpp636
-rw-r--r--engines/sci/engine/kstring.cpp1
-rw-r--r--engines/sci/engine/kvideo.cpp2
-rw-r--r--engines/sci/engine/script.cpp2
-rw-r--r--engines/sci/engine/script_patches.cpp44
-rw-r--r--engines/sci/engine/scriptdebug.cpp5
-rw-r--r--engines/sci/engine/seg_manager.cpp2
-rw-r--r--engines/sci/engine/vm.cpp2
-rw-r--r--engines/sci/graphics/controls32.cpp6
-rw-r--r--engines/sci/graphics/frameout.cpp14
-rw-r--r--engines/sci/graphics/paint16.cpp4
-rw-r--r--engines/sci/graphics/ports.cpp41
-rw-r--r--engines/sci/sci.cpp1
-rw-r--r--engines/sci/sci.h20
-rw-r--r--engines/sci/sound/drivers/adlib.cpp3
-rw-r--r--engines/sci/sound/drivers/fmtowns.cpp12
-rw-r--r--engines/sci/sound/midiparser_sci.cpp80
-rw-r--r--engines/sci/sound/midiparser_sci.h2
-rw-r--r--engines/sci/sound/music.cpp8
-rw-r--r--engines/sci/video/robot_decoder.cpp6
-rw-r--r--engines/sci/video/robot_decoder.h4
-rw-r--r--engines/scumm/actor.cpp30
-rw-r--r--engines/scumm/actor.h2
-rw-r--r--engines/scumm/charset.cpp8
-rw-r--r--engines/scumm/charset.h4
-rw-r--r--engines/scumm/costume.cpp6
-rw-r--r--engines/scumm/cursor.cpp2
-rw-r--r--engines/scumm/debugger.cpp2
-rw-r--r--engines/scumm/detection.cpp9
-rw-r--r--engines/scumm/detection_tables.h2
-rw-r--r--engines/scumm/dialogs.cpp2
-rw-r--r--engines/scumm/he/logic/soccer.cpp4
-rw-r--r--engines/scumm/he/wiz_he.cpp3
-rw-r--r--engines/scumm/imuse/imuse.cpp41
-rw-r--r--engines/scumm/imuse/imuse_internal.h2
-rw-r--r--engines/scumm/imuse/imuse_part.cpp13
-rw-r--r--engines/scumm/imuse/imuse_player.cpp5
-rw-r--r--engines/scumm/imuse/instrument.cpp60
-rw-r--r--engines/scumm/imuse/instrument.h4
-rw-r--r--engines/scumm/imuse/mac_m68k.cpp514
-rw-r--r--engines/scumm/imuse/mac_m68k.h177
-rw-r--r--engines/scumm/imuse/sysex_scumm.cpp2
-rw-r--r--engines/scumm/midiparser_ro.cpp16
-rw-r--r--engines/scumm/module.mk1
-rw-r--r--engines/scumm/object.cpp4
-rw-r--r--engines/scumm/player_apple2.cpp68
-rw-r--r--engines/scumm/player_apple2.h10
-rw-r--r--engines/scumm/player_towns.cpp2
-rw-r--r--engines/scumm/player_v2cms.cpp64
-rw-r--r--engines/scumm/proc3ARM.s89
-rw-r--r--engines/scumm/saveload.cpp2
-rw-r--r--engines/scumm/saveload.h2
-rw-r--r--engines/scumm/script_v0.cpp12
-rw-r--r--engines/scumm/script_v2.cpp6
-rw-r--r--engines/scumm/script_v5.cpp2
-rw-r--r--engines/scumm/scumm.cpp37
-rw-r--r--engines/scumm/scumm.h13
-rw-r--r--engines/scumm/sound.cpp9
-rw-r--r--engines/scumm/verbs.cpp4
-rw-r--r--engines/sky/detection.cpp4
-rw-r--r--engines/sword1/animation.cpp6
-rw-r--r--engines/sword1/control.cpp4
-rw-r--r--engines/sword1/objectman.cpp4
-rw-r--r--engines/sword1/sword1.cpp4
-rw-r--r--engines/sword1/sword1.h1
-rw-r--r--engines/sword2/sprite.cpp4
-rw-r--r--engines/sword25/sfx/soundengine.cpp2
-rw-r--r--engines/tinsel/actors.cpp4
-rw-r--r--engines/tinsel/pcode.cpp4
-rw-r--r--engines/tinsel/saveload.cpp2
-rw-r--r--engines/tinsel/scene.cpp3
-rw-r--r--engines/toltecs/animation.cpp14
-rw-r--r--engines/toltecs/animation.h2
-rw-r--r--engines/toltecs/menu.cpp110
-rw-r--r--engines/toltecs/menu.h21
-rw-r--r--engines/toltecs/microtiles.cpp2
-rw-r--r--engines/toltecs/movie.cpp58
-rw-r--r--engines/toltecs/movie.h8
-rw-r--r--engines/toltecs/music.cpp20
-rw-r--r--engines/toltecs/music.h2
-rw-r--r--engines/toltecs/palette.cpp18
-rw-r--r--engines/toltecs/palette.h2
-rw-r--r--engines/toltecs/render.cpp14
-rw-r--r--engines/toltecs/render.h4
-rw-r--r--engines/toltecs/resource.cpp8
-rw-r--r--engines/toltecs/saveload.cpp17
-rw-r--r--engines/toltecs/screen.cpp49
-rw-r--r--engines/toltecs/screen.h19
-rw-r--r--engines/toltecs/script.cpp41
-rw-r--r--engines/toltecs/script.h6
-rw-r--r--engines/toltecs/segmap.cpp20
-rw-r--r--engines/toltecs/segmap.h8
-rw-r--r--engines/toltecs/sound.cpp107
-rw-r--r--engines/toltecs/sound.h7
-rw-r--r--engines/toltecs/sprite.cpp20
-rw-r--r--engines/toltecs/toltecs.cpp82
-rw-r--r--engines/toltecs/toltecs.h23
-rw-r--r--engines/tony/custom.cpp2530
-rw-r--r--engines/tony/custom.h85
-rw-r--r--engines/tony/debugger.cpp130
-rw-r--r--engines/tony/debugger.h43
-rw-r--r--engines/tony/detection.cpp194
-rw-r--r--engines/tony/detection_tables.h178
-rw-r--r--engines/tony/font.cpp1179
-rw-r--r--engines/tony/font.h379
-rw-r--r--engines/tony/game.cpp1604
-rw-r--r--engines/tony/game.h340
-rw-r--r--engines/tony/gfxcore.cpp2192
-rw-r--r--engines/tony/gfxcore.h516
-rw-r--r--engines/tony/gfxengine.cpp843
-rw-r--r--engines/tony/gfxengine.h139
-rw-r--r--engines/tony/globals.cpp135
-rw-r--r--engines/tony/globals.h286
-rw-r--r--engines/tony/input.cpp157
-rw-r--r--engines/tony/input.h88
-rw-r--r--engines/tony/inventory.cpp938
-rw-r--r--engines/tony/inventory.h241
-rw-r--r--engines/tony/loc.cpp2280
-rw-r--r--engines/tony/loc.h582
-rw-r--r--engines/tony/module.mk33
-rw-r--r--engines/tony/mpal/expr.cpp365
-rw-r--r--engines/tony/mpal/expr.h140
-rw-r--r--engines/tony/mpal/loadmpc.cpp788
-rw-r--r--engines/tony/mpal/loadmpc.h59
-rw-r--r--engines/tony/mpal/lzo.cpp511
-rw-r--r--engines/tony/mpal/lzo.h111
-rw-r--r--engines/tony/mpal/memory.cpp127
-rw-r--r--engines/tony/mpal/memory.h78
-rw-r--r--engines/tony/mpal/mpal.cpp2089
-rw-r--r--engines/tony/mpal/mpal.h518
-rw-r--r--engines/tony/mpal/mpaldll.h251
-rw-r--r--engines/tony/mpal/mpalutils.cpp115
-rw-r--r--engines/tony/mpal/mpalutils.h74
-rw-r--r--engines/tony/resid.h71
-rw-r--r--engines/tony/sound.cpp688
-rw-r--r--engines/tony/sound.h377
-rw-r--r--engines/tony/tony.cpp791
-rw-r--r--engines/tony/tony.h242
-rw-r--r--engines/tony/tonychar.cpp1945
-rw-r--r--engines/tony/tonychar.h482
-rw-r--r--engines/tony/utils.cpp448
-rw-r--r--engines/tony/utils.h176
-rw-r--r--engines/tony/window.cpp336
-rw-r--r--engines/tony/window.h98
-rw-r--r--engines/toon/detection.cpp2
-rw-r--r--engines/toon/movie.h2
-rw-r--r--engines/toon/picture.cpp2
-rw-r--r--engines/touche/staticres.cpp5
-rw-r--r--engines/tsage/blue_force/blueforce_scenes3.cpp8
-rw-r--r--engines/tsage/blue_force/blueforce_scenes7.cpp2
-rw-r--r--engines/tsage/blue_force/blueforce_speakers.cpp28
-rw-r--r--engines/tsage/blue_force/blueforce_speakers.h6
-rw-r--r--engines/tsage/detection.cpp2
-rw-r--r--engines/tsage/globals.cpp8
-rw-r--r--engines/tsage/ringworld/ringworld_logic.cpp2
-rw-r--r--engines/tsage/ringworld2/ringworld2_logic.cpp6
-rw-r--r--engines/tsage/scenes.h2
-rw-r--r--engines/tsage/sound.cpp266
-rw-r--r--engines/tsage/sound.h102
-rw-r--r--engines/tsage/tsage.cpp6
-rw-r--r--engines/tucker/resource.cpp33
-rw-r--r--engines/wintermute/ad/ad_actor.cpp1460
-rw-r--r--engines/wintermute/ad/ad_actor.h108
-rw-r--r--engines/wintermute/ad/ad_entity.cpp1122
-rw-r--r--engines/wintermute/ad/ad_entity.h68
-rw-r--r--engines/wintermute/ad/ad_game.cpp2281
-rw-r--r--engines/wintermute/ad/ad_game.h163
-rw-r--r--engines/wintermute/ad/ad_inventory.cpp136
-rw-r--r--engines/wintermute/ad/ad_inventory.h52
-rw-r--r--engines/wintermute/ad/ad_inventory_box.cpp389
-rw-r--r--engines/wintermute/ad/ad_inventory_box.h65
-rw-r--r--engines/wintermute/ad/ad_item.cpp813
-rw-r--r--engines/wintermute/ad/ad_item.h69
-rw-r--r--engines/wintermute/ad/ad_layer.cpp564
-rw-r--r--engines/wintermute/ad/ad_layer.h58
-rw-r--r--engines/wintermute/ad/ad_node_state.cpp196
-rw-r--r--engines/wintermute/ad/ad_node_state.h60
-rw-r--r--engines/wintermute/ad/ad_object.cpp1300
-rw-r--r--engines/wintermute/ad/ad_object.h124
-rw-r--r--engines/wintermute/ad/ad_path.cpp120
-rw-r--r--engines/wintermute/ad/ad_path.h56
-rw-r--r--engines/wintermute/ad/ad_path_point.cpp75
-rw-r--r--engines/wintermute/ad/ad_path_point.h50
-rw-r--r--engines/wintermute/ad/ad_region.cpp397
-rw-r--r--engines/wintermute/ad/ad_region.h58
-rw-r--r--engines/wintermute/ad/ad_response.cpp146
-rw-r--r--engines/wintermute/ad/ad_response.h61
-rw-r--r--engines/wintermute/ad/ad_response_box.cpp713
-rw-r--r--engines/wintermute/ad/ad_response_box.h87
-rw-r--r--engines/wintermute/ad/ad_response_context.cpp71
-rw-r--r--engines/wintermute/ad/ad_response_context.h50
-rw-r--r--engines/wintermute/ad/ad_rot_level.cpp161
-rw-r--r--engines/wintermute/ad/ad_rot_level.h49
-rw-r--r--engines/wintermute/ad/ad_scale_level.cpp159
-rw-r--r--engines/wintermute/ad/ad_scale_level.h50
-rw-r--r--engines/wintermute/ad/ad_scene.cpp2987
-rw-r--r--engines/wintermute/ad/ad_scene.h181
-rw-r--r--engines/wintermute/ad/ad_scene_node.cpp82
-rw-r--r--engines/wintermute/ad/ad_scene_node.h54
-rw-r--r--engines/wintermute/ad/ad_scene_state.cpp95
-rw-r--r--engines/wintermute/ad/ad_scene_state.h51
-rw-r--r--engines/wintermute/ad/ad_sentence.cpp361
-rw-r--r--engines/wintermute/ad/ad_sentence.h85
-rw-r--r--engines/wintermute/ad/ad_sprite_set.cpp356
-rw-r--r--engines/wintermute/ad/ad_sprite_set.h53
-rw-r--r--engines/wintermute/ad/ad_talk_def.cpp285
-rw-r--r--engines/wintermute/ad/ad_talk_def.h58
-rw-r--r--engines/wintermute/ad/ad_talk_holder.cpp402
-rw-r--r--engines/wintermute/ad/ad_talk_holder.h57
-rw-r--r--engines/wintermute/ad/ad_talk_node.cpp295
-rw-r--r--engines/wintermute/ad/ad_talk_node.h63
-rw-r--r--engines/wintermute/ad/ad_types.h107
-rw-r--r--engines/wintermute/ad/ad_waypoint_group.cpp270
-rw-r--r--engines/wintermute/ad/ad_waypoint_group.h58
-rw-r--r--engines/wintermute/base/base.cpp185
-rw-r--r--engines/wintermute/base/base.h62
-rw-r--r--engines/wintermute/base/base_active_rect.cpp111
-rw-r--r--engines/wintermute/base/base_active_rect.h60
-rw-r--r--engines/wintermute/base/base_dynamic_buffer.cpp204
-rw-r--r--engines/wintermute/base/base_dynamic_buffer.h65
-rw-r--r--engines/wintermute/base/base_engine.cpp93
-rw-r--r--engines/wintermute/base/base_engine.h69
-rw-r--r--engines/wintermute/base/base_fader.cpp195
-rw-r--r--engines/wintermute/base/base_fader.h63
-rw-r--r--engines/wintermute/base/base_file_manager.cpp340
-rw-r--r--engines/wintermute/base/base_file_manager.h76
-rw-r--r--engines/wintermute/base/base_frame.cpp766
-rw-r--r--engines/wintermute/base/base_frame.h75
-rw-r--r--engines/wintermute/base/base_game.cpp4483
-rw-r--r--engines/wintermute/base/base_game.h361
-rw-r--r--engines/wintermute/base/base_keyboard_state.cpp309
-rw-r--r--engines/wintermute/base/base_keyboard_state.h75
-rw-r--r--engines/wintermute/base/base_named_object.cpp71
-rw-r--r--engines/wintermute/base/base_named_object.h51
-rw-r--r--engines/wintermute/base/base_object.cpp1246
-rw-r--r--engines/wintermute/base/base_object.h147
-rw-r--r--engines/wintermute/base/base_parser.cpp467
-rw-r--r--engines/wintermute/base/base_parser.h88
-rw-r--r--engines/wintermute/base/base_persistence_manager.cpp828
-rw-r--r--engines/wintermute/base/base_persistence_manager.h118
-rw-r--r--engines/wintermute/base/base_point.cpp63
-rw-r--r--engines/wintermute/base/base_point.h50
-rw-r--r--engines/wintermute/base/base_quick_msg.cpp57
-rw-r--r--engines/wintermute/base/base_quick_msg.h48
-rw-r--r--engines/wintermute/base/base_region.cpp535
-rw-r--r--engines/wintermute/base/base_region.h70
-rw-r--r--engines/wintermute/base/base_save_thumb_helper.cpp81
-rw-r--r--engines/wintermute/base/base_save_thumb_helper.h51
-rw-r--r--engines/wintermute/base/base_script_holder.cpp502
-rw-r--r--engines/wintermute/base/base_script_holder.h76
-rw-r--r--engines/wintermute/base/base_scriptable.cpp191
-rw-r--r--engines/wintermute/base/base_scriptable.h83
-rw-r--r--engines/wintermute/base/base_sprite.cpp819
-rw-r--r--engines/wintermute/base/base_sprite.h93
-rw-r--r--engines/wintermute/base/base_string_table.cpp255
-rw-r--r--engines/wintermute/base/base_string_table.h55
-rw-r--r--engines/wintermute/base/base_sub_frame.cpp659
-rw-r--r--engines/wintermute/base/base_sub_frame.h93
-rw-r--r--engines/wintermute/base/base_surface_storage.cpp208
-rw-r--r--engines/wintermute/base/base_surface_storage.h57
-rw-r--r--engines/wintermute/base/base_transition_manager.cpp137
-rw-r--r--engines/wintermute/base/base_transition_manager.h54
-rw-r--r--engines/wintermute/base/base_viewport.cpp99
-rw-r--r--engines/wintermute/base/base_viewport.h55
-rw-r--r--engines/wintermute/base/file/base_disk_file.cpp194
-rw-r--r--engines/wintermute/base/file/base_disk_file.h41
-rw-r--r--engines/wintermute/base/file/base_file.cpp68
-rw-r--r--engines/wintermute/base/file/base_file.h67
-rw-r--r--engines/wintermute/base/file/base_file_entry.cpp73
-rw-r--r--engines/wintermute/base/file/base_file_entry.h60
-rw-r--r--engines/wintermute/base/file/base_package.cpp276
-rw-r--r--engines/wintermute/base/file/base_package.h90
-rw-r--r--engines/wintermute/base/file/base_resources.cpp2830
-rw-r--r--engines/wintermute/base/file/base_resources.h45
-rw-r--r--engines/wintermute/base/file/base_save_thumb_file.cpp153
-rw-r--r--engines/wintermute/base/file/base_save_thumb_file.h52
-rw-r--r--engines/wintermute/base/file/dcpackage.h80
-rw-r--r--engines/wintermute/base/font/base_font.cpp140
-rw-r--r--engines/wintermute/base/font/base_font.h61
-rw-r--r--engines/wintermute/base/font/base_font_bitmap.cpp590
-rw-r--r--engines/wintermute/base/font/base_font_bitmap.h71
-rw-r--r--engines/wintermute/base/font/base_font_storage.cpp141
-rw-r--r--engines/wintermute/base/font/base_font_storage.h55
-rw-r--r--engines/wintermute/base/font/base_font_truetype.cpp602
-rw-r--r--engines/wintermute/base/font/base_font_truetype.h152
-rw-r--r--engines/wintermute/base/gfx/base_image.cpp231
-rw-r--r--engines/wintermute/base/gfx/base_image.h72
-rw-r--r--engines/wintermute/base/gfx/base_renderer.cpp381
-rw-r--r--engines/wintermute/base/gfx/base_renderer.h221
-rw-r--r--engines/wintermute/base/gfx/base_surface.cpp149
-rw-r--r--engines/wintermute/base/gfx/base_surface.h99
-rw-r--r--engines/wintermute/base/gfx/osystem/base_render_osystem.cpp609
-rw-r--r--engines/wintermute/base/gfx/osystem/base_render_osystem.h131
-rw-r--r--engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp428
-rw-r--r--engines/wintermute/base/gfx/osystem/base_surface_osystem.h99
-rw-r--r--engines/wintermute/base/particles/part_emitter.cpp1257
-rw-r--r--engines/wintermute/base/particles/part_emitter.h140
-rw-r--r--engines/wintermute/base/particles/part_force.cpp65
-rw-r--r--engines/wintermute/base/particles/part_force.h57
-rw-r--r--engines/wintermute/base/particles/part_particle.cpp269
-rw-r--r--engines/wintermute/base/particles/part_particle.h90
-rw-r--r--engines/wintermute/base/saveload.cpp204
-rw-r--r--engines/wintermute/base/saveload.h57
-rw-r--r--engines/wintermute/base/scriptables/dcscript.h141
-rw-r--r--engines/wintermute/base/scriptables/script.cpp1467
-rw-r--r--engines/wintermute/base/scriptables/script.h174
-rw-r--r--engines/wintermute/base/scriptables/script_engine.cpp609
-rw-r--r--engines/wintermute/base/scriptables/script_engine.h135
-rw-r--r--engines/wintermute/base/scriptables/script_ext_array.cpp252
-rw-r--r--engines/wintermute/base/scriptables/script_ext_array.h56
-rw-r--r--engines/wintermute/base/scriptables/script_ext_date.cpp293
-rw-r--r--engines/wintermute/base/scriptables/script_ext_date.h54
-rw-r--r--engines/wintermute/base/scriptables/script_ext_file.cpp828
-rw-r--r--engines/wintermute/base/scriptables/script_ext_file.h66
-rw-r--r--engines/wintermute/base/scriptables/script_ext_math.cpp295
-rw-r--r--engines/wintermute/base/scriptables/script_ext_math.h53
-rw-r--r--engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp529
-rw-r--r--engines/wintermute/base/scriptables/script_ext_mem_buffer.h60
-rw-r--r--engines/wintermute/base/scriptables/script_ext_object.cpp67
-rw-r--r--engines/wintermute/base/scriptables/script_ext_object.h46
-rw-r--r--engines/wintermute/base/scriptables/script_ext_string.cpp436
-rw-r--r--engines/wintermute/base/scriptables/script_ext_string.h58
-rw-r--r--engines/wintermute/base/scriptables/script_stack.cpp232
-rw-r--r--engines/wintermute/base/scriptables/script_stack.h66
-rw-r--r--engines/wintermute/base/scriptables/script_value.cpp995
-rw-r--r--engines/wintermute/base/scriptables/script_value.h113
-rw-r--r--engines/wintermute/base/sound/base_sound.cpp292
-rw-r--r--engines/wintermute/base/sound/base_sound.h87
-rw-r--r--engines/wintermute/base/sound/base_sound_buffer.cpp295
-rw-r--r--engines/wintermute/base/sound/base_sound_buffer.h100
-rw-r--r--engines/wintermute/base/sound/base_sound_manager.cpp283
-rw-r--r--engines/wintermute/base/sound/base_sound_manager.h68
-rw-r--r--engines/wintermute/coll_templ.h86
-rw-r--r--engines/wintermute/dcgf.h51
-rw-r--r--engines/wintermute/dctypes.h225
-rw-r--r--engines/wintermute/detection.cpp174
-rw-r--r--engines/wintermute/detection_tables.h265
-rw-r--r--engines/wintermute/graphics/transparent_surface.cpp440
-rw-r--r--engines/wintermute/graphics/transparent_surface.h131
-rw-r--r--engines/wintermute/math/math_util.cpp52
-rw-r--r--engines/wintermute/math/math_util.h42
-rw-r--r--engines/wintermute/math/matrix4.cpp86
-rw-r--r--engines/wintermute/math/matrix4.h50
-rw-r--r--engines/wintermute/math/rect32.h94
-rw-r--r--engines/wintermute/math/vector2.cpp55
-rw-r--r--engines/wintermute/math/vector2.h75
-rw-r--r--engines/wintermute/module.mk123
-rw-r--r--engines/wintermute/persistent.cpp168
-rw-r--r--engines/wintermute/persistent.h89
-rw-r--r--engines/wintermute/platform_osystem.cpp262
-rw-r--r--engines/wintermute/platform_osystem.h73
-rw-r--r--engines/wintermute/system/sys_class.cpp220
-rw-r--r--engines/wintermute/system/sys_class.h130
-rw-r--r--engines/wintermute/system/sys_class_registry.cpp336
-rw-r--r--engines/wintermute/system/sys_class_registry.h106
-rw-r--r--engines/wintermute/system/sys_instance.cpp49
-rw-r--r--engines/wintermute/system/sys_instance.h68
-rw-r--r--engines/wintermute/ui/ui_button.cpp1209
-rw-r--r--engines/wintermute/ui/ui_button.h80
-rw-r--r--engines/wintermute/ui/ui_edit.cpp952
-rw-r--r--engines/wintermute/ui/ui_edit.h72
-rw-r--r--engines/wintermute/ui/ui_entity.cpp366
-rw-r--r--engines/wintermute/ui/ui_entity.h59
-rw-r--r--engines/wintermute/ui/ui_object.cpp651
-rw-r--r--engines/wintermute/ui/ui_object.h85
-rw-r--r--engines/wintermute/ui/ui_text.cpp522
-rw-r--r--engines/wintermute/ui/ui_text.h60
-rw-r--r--engines/wintermute/ui/ui_tiled_image.cpp394
-rw-r--r--engines/wintermute/ui/ui_tiled_image.h63
-rw-r--r--engines/wintermute/ui/ui_window.cpp1445
-rw-r--r--engines/wintermute/ui/ui_window.h94
-rw-r--r--engines/wintermute/utils/convert_utf.cpp615
-rw-r--r--engines/wintermute/utils/convert_utf.h148
-rw-r--r--engines/wintermute/utils/crc.cpp237
-rw-r--r--engines/wintermute/utils/crc.h85
-rw-r--r--engines/wintermute/utils/path_util.cpp101
-rw-r--r--engines/wintermute/utils/path_util.h49
-rw-r--r--engines/wintermute/utils/string_util.cpp232
-rw-r--r--engines/wintermute/utils/string_util.h56
-rw-r--r--engines/wintermute/utils/utils.cpp261
-rw-r--r--engines/wintermute/utils/utils.h64
-rw-r--r--engines/wintermute/video/video_player.cpp109
-rw-r--r--engines/wintermute/video/video_player.h90
-rw-r--r--engines/wintermute/video/video_theora_player.cpp505
-rw-r--r--engines/wintermute/video/video_theora_player.h148
-rw-r--r--engines/wintermute/wintermute.cpp383
-rw-r--r--engines/wintermute/wintermute.h78
-rw-r--r--engines/wintermute/wintypes.h53
-rw-r--r--graphics/VectorRenderer.cpp2
-rw-r--r--graphics/VectorRenderer.h2
-rw-r--r--graphics/VectorRendererSpec.cpp64
-rw-r--r--graphics/decoders/bmp.cpp12
-rw-r--r--graphics/decoders/image_decoder.h7
-rw-r--r--graphics/decoders/jpeg.cpp4
-rw-r--r--graphics/decoders/pcx.cpp213
-rw-r--r--graphics/decoders/pcx.h68
-rw-r--r--graphics/decoders/pict.cpp14
-rw-r--r--graphics/decoders/pict.h5
-rw-r--r--graphics/decoders/png.cpp1
-rw-r--r--graphics/decoders/tga.cpp427
-rw-r--r--graphics/decoders/tga.h98
-rw-r--r--graphics/fonts/consolefont.cpp2
-rw-r--r--graphics/fonts/newfont.cpp2
-rw-r--r--graphics/fonts/newfont_big.cpp2
-rw-r--r--graphics/module.mk4
-rw-r--r--graphics/primitives.cpp62
-rw-r--r--graphics/primitives.h2
-rw-r--r--graphics/scaler/aspect.cpp62
-rw-r--r--graphics/sjis.h2
-rw-r--r--graphics/surface.cpp11
-rw-r--r--graphics/surface.h16
-rw-r--r--graphics/yuv_to_rgb.cpp188
-rw-r--r--graphics/yuv_to_rgb.h116
-rw-r--r--gui/ThemeParser.cpp4
-rw-r--r--gui/ThemeParser.h2
-rw-r--r--gui/credits.h22
-rw-r--r--gui/launcher.cpp2
-rw-r--r--gui/options.cpp2
-rw-r--r--gui/predictivedialog.cpp18
-rw-r--r--gui/saveload-dialog.cpp70
-rw-r--r--gui/saveload-dialog.h1
-rw-r--r--gui/widget.cpp2
-rw-r--r--gui/widgets/editable.h4
-rw-r--r--gui/widgets/list.h1
-rw-r--r--ports.mk3
-rw-r--r--video/avi_decoder.cpp2
-rw-r--r--video/avi_decoder.h2
-rw-r--r--video/bink_decoder.cpp26
-rw-r--r--video/bink_decoder.h2
-rw-r--r--video/codecs/svq1.cpp43
-rw-r--r--video/coktel_decoder.cpp2
-rw-r--r--video/psx_decoder.cpp6
-rw-r--r--video/psx_decoder.h2
-rw-r--r--video/qt_decoder.cpp2
-rw-r--r--video/qt_decoder.h1
-rw-r--r--video/smk_decoder.cpp2
-rw-r--r--video/theora_decoder.cpp4
-rw-r--r--video/video_decoder.cpp63
-rw-r--r--video/video_decoder.h20
835 files changed, 152915 insertions, 3934 deletions
diff --git a/AUTHORS b/AUTHORS
index fe806c4490..2ae50159c3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,8 +6,9 @@ ScummVM Team
PR Office
---------
- Arnaud Boutonne - Public Relations Officer, Project Administrator
- Eugene Sandulenko - Project Leader
+ Arnaud Boutonne - Public Relations Officer, Project
+ Administrator
+ Eugene Sandulenko - Project Leader
Core Team
---------
@@ -18,29 +19,30 @@ ScummVM Team
Retired Project Leaders
-----------------------
James Brown
- Vincent Hamm - ScummVM co-founder, Original Cruise/CinE author
+ Vincent Hamm - ScummVM co-founder, Original Cruise/CinE
+ author
Max Horn
- Ludvig Strigeus - Original ScummVM and SimonVM author
+ Ludvig Strigeus - Original ScummVM and SimonVM author
Engine Teams
------------
SCUMM:
Torbjorn Andersson
- James Brown - (retired)
- Jonathan Gray - (retired)
- Vincent Hamm - (retired)
- Max Horn - (retired)
+ James Brown - (retired)
+ Jonathan Gray - (retired)
+ Vincent Hamm - (retired)
+ Max Horn - (retired)
Travis Howell
- Pawel Kolodziejski - Codecs, iMUSE, Smush, etc.
- Gregory Montoir - (retired)
- Eugene Sandulenko - FT INSANE, MM NES, MM C64, game detection,
- Herc/CGA
- Ludvig Strigeus - (retired)
+ Pawel Kolodziejski - Codecs, iMUSE, Smush, etc.
+ Gregory Montoir - (retired)
+ Eugene Sandulenko - FT INSANE, MM NES, MM C64, game detection,
+ Herc/CGA
+ Ludvig Strigeus - (retired)
HE:
- Jonathan Gray - (retired)
+ Jonathan Gray - (retired)
Travis Howell
- Gregory Montoir - (retired)
+ Gregory Montoir - (retired)
Eugene Sandulenko
AGI:
@@ -48,26 +50,26 @@ ScummVM Team
Matthew Hoops
Filippos Karapetis
Pawel Kolodziejski
- Walter van Niftrik - (retired)
+ Walter van Niftrik - (retired)
Kari Salminen
Eugene Sandulenko
- David Symonds - (retired)
+ David Symonds - (retired)
AGOS:
Torbjorn Andersson
Paul Gilbert
Travis Howell
- Oliver Kiehl - (retired)
- Ludvig Strigeus - (retired)
+ Oliver Kiehl - (retired)
+ Ludvig Strigeus - (retired)
CGE:
Arnaud Boutonne
Paul Gilbert
Cine:
- Vincent Hamm - (retired)
+ Vincent Hamm - (retired)
Pawel Kolodziejski
- Gregory Montoir - (retired)
+ Gregory Montoir - (retired)
Kari Salminen
Eugene Sandulenko
@@ -76,7 +78,7 @@ ScummVM Team
CruisE:
Paul Gilbert
- Vincent Hamm - (retired)
+ Vincent Hamm - (retired)
Draci:
Denis Kasak
@@ -90,7 +92,7 @@ ScummVM Team
Torbjorn Andersson
Bertrand Augereau
Filippos Karapetis
- Vladimir Menshakov - (retired)
+ Vladimir Menshakov - (retired)
Willem Jan Palenstijn
Gob:
@@ -110,10 +112,10 @@ ScummVM Team
Eugene Sandulenko
Kyra:
- Torbjorn Andersson - VQA Player
+ Torbjorn Andersson - VQA Player
Oystein Eftevaag
Florian Kagerer
- Gregory Montoir - (retired)
+ Gregory Montoir - (retired)
Johannes Schickel
Lastexpress:
@@ -139,15 +141,18 @@ ScummVM Team
Parallaction:
peres
+ Pegasus:
+ Matthew Hoops
+
Queen:
- David Eriksson - (retired)
- Gregory Montoir - (retired)
+ David Eriksson - (retired)
+ Gregory Montoir - (retired)
Joost Peters
SAGA:
Torbjorn Andersson
- Daniel Balsom - Original engine reimplementation author
- (retired)
+ Daniel Balsom - Original engine reimplementation author
+ (retired)
Filippos Karapetis
Andrew Kurushin
Eugene Sandulenko
@@ -155,61 +160,69 @@ ScummVM Team
SCI:
Greg Frieger
Paul Gilbert
- Max Horn - (retired)
+ Max Horn - (retired)
Filippos Karapetis
Martin Kiewitz
- Walter van Niftrik - (retired)
+ Walter van Niftrik - (retired)
Willem Jan Palenstijn
Jordi Vilalta Prat
Lars Skovlund
Sky:
- Robert Goeffringmann - (retired)
- Oliver Kiehl - (retired)
+ Robert Goeffringmann - (retired)
+ Oliver Kiehl - (retired)
Joost Peters
Sword1:
- Fabio Battaglia - PSX version support
- Thierry Crozat - Mac version support
- Robert Goeffringmann - (retired)
+ Fabio Battaglia - PSX version support
+ Thierry Crozat - Mac version support
+ Robert Goeffringmann - (retired)
Sword2:
Torbjorn Andersson
- Fabio Battaglia - PSX version support
- Jonathan Gray - (retired)
+ Fabio Battaglia - PSX version support
+ Jonathan Gray - (retired)
Sword25:
Torbjorn Andersson
Paul Gilbert
- Max Horn - (retired)
+ Max Horn - (retired)
Filippos Karapetis
Eugene Sandulenko
TeenAgent:
- Robert Megone - Help with callback rewriting
- Vladimir Menshakov - (retired)
+ Robert Megone - Help with callback rewriting
+ Vladimir Menshakov - (retired)
Tinsel:
Torbjorn Andersson
- Fabio Battaglia - PSX version support
+ Fabio Battaglia - PSX version support
Paul Gilbert
Sven Hesse
- Max Horn - (retired)
+ Max Horn - (retired)
Filippos Karapetis
Joost Peters
+ Tony:
+ Arnaud Boutonne
+ Paul Gilbert
+ Alyssa Milburn
+
Toon:
Sylvain Dupont
Touche:
- Gregory Montoir - (retired)
+ Gregory Montoir - (retired)
TsAGE:
Arnaud Boutonne
Paul Gilbert
Tucker:
- Gregory Montoir - (retired)
+ Gregory Montoir - (retired)
+
+ Wintermute:
+ Einar Johan T. Somaaen
Backend Teams
-------------
@@ -233,26 +246,26 @@ ScummVM Team
Lubomyr Lisen
Maemo:
- Frantisek Dufka - (retired)
+ Frantisek Dufka - (retired)
Tarek Soliman
Nintendo 64:
Fabio Battaglia
Nintendo DS:
- Bertrand Augereau - HQ software scaler
+ Bertrand Augereau - HQ software scaler
Neil Millstone
OpenPandora:
John Willis
PocketPC / WinCE:
- Nicolas Bacca - (retired)
+ Nicolas Bacca - (retired)
Ismail Khatib
- Kostas Nakos - (retired)
+ Kostas Nakos - (retired)
PlayStation 2:
- Robert Goeffringmann - (retired)
+ Robert Goeffringmann - (retired)
Max Lingua
PSP (PlayStation Portable):
@@ -260,8 +273,8 @@ ScummVM Team
Joost Peters
SDL (Win/Linux/OS X/etc.):
- Max Horn - (retired)
- Eugene Sandulenko - Asm routines, GFX layers
+ Max Horn - (retired)
+ Eugene Sandulenko - Asm routines, GFX layers
SymbianOS:
Jurgen Braam
@@ -276,8 +289,9 @@ ScummVM Team
Other subsystems
----------------
Infrastructure:
- Max Horn - Backend & Engine APIs, file API, sound mixer,
- audiostreams, data structures, etc. (retired)
+ Max Horn - Backend & Engine APIs, file API, sound
+ mixer, audiostreams, data structures, etc.
+ (retired)
Eugene Sandulenko
Johannes Schickel
@@ -287,30 +301,31 @@ ScummVM Team
Johannes Schickel
Miscellaneous:
- David Corrales-Lopez - Filesystem access improvements (GSoC 2007
- task) (retired)
- Jerome Fisher - MT-32 emulator
- Benjamin Haisch - Heavily improved de-/encoder for DXA videos
- Jochen Hoenicke - Speaker & PCjr sound support, AdLib work
- (retired)
- Chris Page - Return to launcher, savestate improvements,
- leak fixes, ... (GSoC 2008 task) (retired)
- Robin Watts - ARM assembly routines for nice speedups on
- several ports; improvements to the sound mixer
+ David Corrales-Lopez - Filesystem access improvements (GSoC 2007
+ task) (retired)
+ Jerome Fisher - MT-32 emulator
+ Benjamin Haisch - Heavily improved de-/encoder for DXA videos
+ Jochen Hoenicke - Speaker & PCjr sound support, AdLib work
+ (retired)
+ Chris Page - Return to launcher, savestate improvements,
+ leak fixes, ... (GSoC 2008 task) (retired)
+ Robin Watts - ARM assembly routines for nice speedups on
+ several ports; improvements to the sound
+ mixer
Website (code)
--------------
- Fredrik Wendel - (retired)
+ Fredrik Wendel - (retired)
Website (maintenance)
---------------------
- James Brown - IRC Logs maintainer
- Thierry Crozat - Wiki maintainer
- Andre Heider - Buildbot maintainer
- Joost Peters - Doxygen Project Documentation maintainer
- Jordi Vilalta Prat - Wiki maintainer
- Eugene Sandulenko - Forum, IRC channel, Screen Shots and Mailing
- list maintainer
+ James Brown - IRC Logs maintainer
+ Thierry Crozat - Wiki maintainer
+ Andre Heider - Buildbot maintainer
+ Joost Peters - Doxygen Project Documentation maintainer
+ Jordi Vilalta Prat - Wiki maintainer
+ Eugene Sandulenko - Forum, IRC channel, Screen Shots and Mailing
+ list maintainer
John Willis
Website (content)
@@ -319,31 +334,31 @@ ScummVM Team
Documentation
-------------
- Thierry Crozat - Numerous contributions to documentation
- Joachim Eberhard - Numerous contributions to documentation
- (retired)
- Matthew Hoops - Wiki editor
+ Thierry Crozat - Numerous contributions to documentation
+ Joachim Eberhard - Numerous contributions to documentation
+ (retired)
+ Matthew Hoops - Wiki editor
Retired Team Members
--------------------
- Chris Apers - Former PalmOS porter
- Ralph Brorsen - Help with GUI implementation
- Jamieson Christian - iMUSE, MIDI, all things musical
- Felix Jakschitsch - Zak256 reverse engineering
- Mutwin Kraus - Original MacOS porter
- Peter Moraliyski - Port: GP32
- Jeremy Newman - Former webmaster
- Lionel Ulmer - Port: X11
- Won Star - Former GP32 porter
+ Chris Apers - Former PalmOS porter
+ Ralph Brorsen - Help with GUI implementation
+ Jamieson Christian - iMUSE, MIDI, all things musical
+ Felix Jakschitsch - Zak256 reverse engineering
+ Mutwin Kraus - Original MacOS porter
+ Peter Moraliyski - Port: GP32
+ Jeremy Newman - Former webmaster
+ Lionel Ulmer - Port: X11
+ Won Star - Former GP32 porter
Other contributions
*******************
Packages
--------
AmigaOS 4:
- Hans-Joerg Frieden - (retired)
+ Hans-Joerg Frieden - (retired)
Hubert Maier
- Juha Niemimaki - (retired)
+ Juha Niemimaki - (retired)
Atari/FreeMiNT:
Keith Scroggins
@@ -353,22 +368,22 @@ Other contributions
Luc Schrijvers
Debian GNU/Linux:
- Tore Anderson - (retired)
+ Tore Anderson - (retired)
David Weinehall
Fedora / RedHat:
Willem Jan Palenstijn
Mac OS X:
- Max Horn - (retired)
+ Max Horn - (retired)
Oystein Eftevaag
Mandriva:
- Dominik Scherer - (retired)
+ Dominik Scherer - (retired)
MorphOS:
Fabien Coeurjoly
- Ruediger Hanke - (retired)
+ Ruediger Hanke - (retired)
OS/2:
Paul Smedley
@@ -386,12 +401,12 @@ Other contributions
Travis Howell
Win64:
- Chris Gray - (retired)
+ Chris Gray - (retired)
Johannes Schickel
Translations
------------
- Thierry Crozat - Translation Lead
+ Thierry Crozat - Translation Lead
Basque:
Mikel Iturbe Urretxa
@@ -413,7 +428,7 @@ Other contributions
German:
Simon Sawatzki
- Lothar Serra Mari
+ Lothar Serra Mari - (retired)
Hungarian:
Alex Bevilacqua
@@ -449,92 +464,95 @@ Other contributions
Websites (design)
-----------------
- Dobo Balazs - Website design
- William Claydon - Skins for doxygen, buildbot and wiki
- Yaroslav Fedevych - HTML/CSS for the website
- Jean Marc Gimenez - ScummVM logo
- David Jensen - SVG logo conversion
- Raina - ScummVM forum buttons
+ Dobo Balazs - Website design
+ William Claydon - Skins for doxygen, buildbot and wiki
+ Yaroslav Fedevych - HTML/CSS for the website
+ Jean Marc Gimenez - ScummVM logo
+ David Jensen - SVG logo conversion
+ Raina - ScummVM forum buttons
Code contributions
------------------
- Ori Avtalion - Subtitle control options in the GUI; BASS GUI
- fixes
- Stuart Caie - Decoders for Amiga and AtariST data files (AGOS
- engine)
- Paolo Costabel - PSP port contributions
- Martin Doucha - CinE engine objectification
- Thomas Fach-Pedersen - ProTracker module player, Smacker video decoder
- Tobias Gunkel - Sound support for C64 version of MM/Zak, Loom
- PCE support
- Janne Huttunen - V3 actor mask support, Dig/FT SMUSH audio
- Kovacs Endre Janos - Several fixes for Simon1
- Jeroen Janssen - Numerous readability and bugfix patches
- Andreas Karlsson - Initial port for SymbianOS
- Claudio Matsuoka - Daily Linux builds
- Thomas Mayer - PSP port contributions
- Sean Murray - ScummVM tools GUI application (GSoC 2007 task)
- n0p - Windows CE port aspect ratio correction scaler
- and right click input method
- Mikesch Nepomuk - MI1 VGA floppy patches
- Nicolas Noble - Config file and ALSA support
- Tim Phillips - Initial MI1 CD music support
- Quietust - Sound support for Amiga SCUMM V2/V3 games, MM
- NES support
- Robert Crossfield - Improved support for Apple II/C64 versions of MM
- Andreas Roever - Broken Sword I & II MPEG2 cutscene support
- Edward Rudd - Fixes for playing MP3 versions of MI1/Loom audio
- Daniel Schepler - Final MI1 CD music support, initial Ogg Vorbis
- support
- Andre Souza - SDL-based OpenGL renderer
- Tom Frost - WebOS port contributions
+ Ori Avtalion - Subtitle control options in the GUI; BASS GUI
+ fixes
+ Stuart Caie - Decoders for Amiga and AtariST data files
+ (AGOS engine)
+ Paolo Costabel - PSP port contributions
+ Martin Doucha - CinE engine objectification
+ Thomas Fach-Pedersen - ProTracker module player, Smacker video
+ decoder
+ Tobias Gunkel - Sound support for C64 version of MM/Zak, Loom
+ PCE support
+ Janne Huttunen - V3 actor mask support, Dig/FT SMUSH audio
+ Kovacs Endre Janos - Several fixes for Simon1
+ Jeroen Janssen - Numerous readability and bugfix patches
+ Andreas Karlsson - Initial port for SymbianOS
+ Claudio Matsuoka - Daily Linux builds
+ Thomas Mayer - PSP port contributions
+ Sean Murray - ScummVM tools GUI application (GSoC 2007 task)
+ n0p - Windows CE port aspect ratio correction scaler
+ and right click input method
+ Mikesch Nepomuk - MI1 VGA floppy patches
+ Nicolas Noble - Config file and ALSA support
+ Tim Phillips - Initial MI1 CD music support
+ Quietust - Sound support for Amiga SCUMM V2/V3 games, MM
+ NES support
+ Robert Crossfield - Improved support for Apple II/C64 versions of
+ MM
+ Andreas Roever - Broken Sword I & II MPEG2 cutscene support
+ Edward Rudd - Fixes for playing MP3 versions of MI1/Loom
+ audio
+ Daniel Schepler - Final MI1 CD music support, initial Ogg Vorbis
+ support
+ Andre Souza - SDL-based OpenGL renderer
+ Tom Frost - WebOS port contributions
FreeSCI Contributors
--------------------
- Francois-R Boyer - MT-32 information and mapping code
- Rainer Canavan - IRIX MIDI driver and bug fixes
+ Francois-R Boyer - MT-32 information and mapping code
+ Rainer Canavan - IRIX MIDI driver and bug fixes
Xiaojun Chen
- Paul David Doherty - Game version information
- Vyacheslav Dikonov - Config script improvements
- Ruediger Hanke - Port to the MorphOS platform
- Matt Hargett - Clean-ups, bugfixes, Hardcore QA, Win32
- Max Horn - SetJump implementation
- Ravi I. - SCI0 sound resource specification
- Emmanuel Jeandel - Bugfixes and bug reports
- Dmitry Jemerov - Port to the Win32 platform, numerous bugfixes
- Chris Kehler - Makefile enhancements
- Christopher T. Lansdo - Original CVS maintainer, Alpha compatibility
- fixes
- Sergey Lapin - Port of Carl's type 2 decompression code
- Rickard Lind - MT-32->GM MIDI mapping magic, sound research
- Hubert Maier - AmigaOS 4 port
- Johannes Manhave - Document format translation
- Claudio Matsuoka - CVS snapshots, daily builds, BeOS and cygwin
- ports
- Dark Minister - SCI research (bytecode and parser)
- Carl Muckenhoupt - Sources to the SCI resource viewer tools that
- started it all
- Anders Baden Nielsen - PPC testing
- Walter van Niftrik - Ports to the Dreamcast and GP32 platforms
- Rune Orsval - Configuration file editor
- Solomon Peachy - SDL ports and much of the sound subsystem
- Robey Pointer - Bug tracking system hosting
- Magnus Reftel - Heap implementation, Python class viewer,
- bugfixes
- Christoph Reichenbach - UN*X code, VM/Graphics/Sound/other
- infrastructure
- George Reid - FreeBSD package management
- Lars Skovlund - Project maintenance, most documentation,
- bugfixes, SCI1 support
- Rink Springer - Port to the DOS platform, several bug fixes
- Rainer De Temple - SCI research
+ Paul David Doherty - Game version information
+ Vyacheslav Dikonov - Config script improvements
+ Ruediger Hanke - Port to the MorphOS platform
+ Matt Hargett - Clean-ups, bugfixes, Hardcore QA, Win32
+ Max Horn - SetJump implementation
+ Ravi I. - SCI0 sound resource specification
+ Emmanuel Jeandel - Bugfixes and bug reports
+ Dmitry Jemerov - Port to the Win32 platform, numerous bugfixes
+ Chris Kehler - Makefile enhancements
+ Christopher T. Lansdown - Original CVS maintainer, Alpha compatibility
+ fixes
+ Sergey Lapin - Port of Carl's type 2 decompression code
+ Rickard Lind - MT-32->GM MIDI mapping magic, sound research
+ Hubert Maier - AmigaOS 4 port
+ Johannes Manhave - Document format translation
+ Claudio Matsuoka - CVS snapshots, daily builds, BeOS and cygwin
+ ports
+ Dark Minister - SCI research (bytecode and parser)
+ Carl Muckenhoupt - Sources to the SCI resource viewer tools that
+ started it all
+ Anders Baden Nielsen - PPC testing
+ Walter van Niftrik - Ports to the Dreamcast and GP32 platforms
+ Rune Orsval - Configuration file editor
+ Solomon Peachy - SDL ports and much of the sound subsystem
+ Robey Pointer - Bug tracking system hosting
+ Magnus Reftel - Heap implementation, Python class viewer,
+ bugfixes
+ Christoph Reichenbach - UN*X code, VM/Graphics/Sound/other
+ infrastructure
+ George Reid - FreeBSD package management
+ Lars Skovlund - Project maintenance, most documentation,
+ bugfixes, SCI1 support
+ Rink Springer - Port to the DOS platform, several bug fixes
+ Rainer De Temple - SCI research
Sean Terrell
- Hugues Valois - Game selection menu
- Jordi Vilalta - Numerous code and website clean-up patches
- Petr Vyhnak - The DCL-INFLATE algorithm, many Win32
- improvements
- Bas Zoetekouw - Man pages, debian package management, CVS
- maintenance
+ Hugues Valois - Game selection menu
+ Jordi Vilalta - Numerous code and website clean-up patches
+ Petr Vyhnak - The DCL-INFLATE algorithm, many Win32
+ improvements
+ Bas Zoetekouw - Man pages, debian package management, CVS
+ maintenance
Special thanks to Prof. Dr. Gary Nutt for allowing the FreeSCI VM
extension as a course project in his Advanced OS course.
@@ -546,28 +564,27 @@ Other contributions
Special thanks to
*****************
- Daniel Balsom - For the original Reinherit (SAGA) code
- Sander Buskens - For his work on the initial reversing of Monkey2
- Canadacow - For the original MT-32 emulator
- Kevin Carnes - For Scumm16, the basis of ScummVM's older gfx codecs
- Curt Coder - For the original TrollVM (preAGI) code
- Patrick Combet - For the original Gobliiins ADL player
- Ivan Dubrov - For contributing the initial version of the Gobliiins
- engine
- Henrik Engqvist - For generously providing hosting for our buildbot, SVN
- repository, planet and doxygen sites as well as tons of
- HD space
- DOSBox Team - For their awesome OPL2 and OPL3 emulator
- Till Kresslein - For design of modern ScummVM GUI
- Jezar - For his freeverb filter implementation
- Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports
- lloyd - For deep tech details about C64 Zak & MM
- Sarien Team - Original AGI engine code
- Jimmi Thogersen - For ScummRev, and much obscure code/documentation
- Tristan - For additional work on the original MT-32 emulator
- James Woodcock - Soundtrack enhancements
-
- Some icons by Yusuke Kamiyamane
+ Daniel Balsom - For the original Reinherit (SAGA) code
+ Sander Buskens - For his work on the initial reversing of Monkey2
+ Canadacow - For the original MT-32 emulator
+ Kevin Carnes - For Scumm16, the basis of ScummVM's older gfx codecs
+ Curt Coder - For the original TrollVM (preAGI) code
+ Patrick Combet - For the original Gobliiins ADL player
+ Ivan Dubrov - For contributing the initial version of the Gobliiins
+ engine
+ Henrik Engqvist - For generously providing hosting for our buildbot, SVN
+ repository, planet and doxygen sites as well as tons
+ of HD space
+ DOSBox Team - For their awesome OPL2 and OPL3 emulator
+ Yusuke Kamiyamane - For contributing some GUI icons
+ Till Kresslein - For design of modern ScummVM GUI
+ Jezar - For his freeverb filter implementation
+ Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports
+ lloyd - For deep tech details about C64 Zak & MM
+ Sarien Team - Original AGI engine code
+ Jimmi Thogersen - For ScummRev, and much obscure code/documentation
+ Tristan - For additional work on the original MT-32 emulator
+ James Woodcock - Soundtrack enhancements
Tony Warriner and everyone at Revolution Software Ltd. for sharing with us
the source of some of their brilliant games, allowing us to release
@@ -606,6 +623,12 @@ Special thanks to
of Dreamweb and for their tremendous support.
Janusz Wisniewski and Miroslaw Liminowicz from Laboratorium Komputerowe
- Avalon for providing full source code for Soltys and letting us to
+ Avalon for providing full source code for Soltys and letting us
redistribute the game.
+ Jan Nedoma for providing the sources to the Wintermute-engine, and for his
+ support while porting the engine to ScummVM.
+
+ Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for providing
+ the source code of The Journeyman Project: Pegasus Prime.
+
diff --git a/NEWS b/NEWS
index 1f736aa74f..923844e32c 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,16 @@ For a more comprehensive changelog of the latest experimental code, see:
- Rewrote VideoDecoder subsystem.
- Added Galician translation.
+ Cine:
+ - Improved audio support for Amiga and AtariST versions of Future Wars.
+ Now music fades out slowly instead of stopping immediately. Sound
+ effects are now properly panned, when requested by the game.
+
+ SCUMM:
+ - Implemented Monkey Island 2 Macintosh's audio driver. Now we properly
+ support its sample based audio output. The same output is also used for
+ the m68k Macintosh version of Indiana Jones and the Fate of Atlantis.
+
1.5.0 (2012-07-27)
New Games:
- Added support for Backyard Baseball 2003.
diff --git a/audio/audiostream.cpp b/audio/audiostream.cpp
index 6e185702f0..2d65d4afef 100644
--- a/audio/audiostream.cpp
+++ b/audio/audiostream.cpp
@@ -402,7 +402,7 @@ public:
}
int readBuffer(int16 *buffer, const int numSamples) {
- // Cap us off so we don't read past _totalSamples
+ // Cap us off so we don't read past _totalSamples
int samplesRead = _parentStream->readBuffer(buffer, MIN<int>(numSamples, _totalSamples - _samplesRead));
_samplesRead += samplesRead;
return samplesRead;
@@ -413,7 +413,7 @@ public:
int getRate() const { return _parentStream->getRate(); }
private:
- int getChannels() const { return isStereo() ? 2 : 1; }
+ int getChannels() const { return isStereo() ? 2 : 1; }
AudioStream *_parentStream;
DisposeAfterUse::Flag _disposeAfterUse;
diff --git a/audio/decoders/aiff.h b/audio/decoders/aiff.h
index 59664bb85a..afcdb6ae6c 100644
--- a/audio/decoders/aiff.h
+++ b/audio/decoders/aiff.h
@@ -23,6 +23,7 @@
/**
* @file
* Sound decoder used in engines:
+ * - pegasus
* - saga
* - sci
* - sword1
diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp
index 5276cfc530..0588650ec6 100644
--- a/audio/decoders/quicktime.cpp
+++ b/audio/decoders/quicktime.cpp
@@ -195,7 +195,7 @@ QuickTimeAudioDecoder::QuickTimeAudioTrack::QuickTimeAudioTrack(QuickTimeAudioDe
if (entry->getCodecTag() == MKTAG('r', 'a', 'w', ' ') || entry->getCodecTag() == MKTAG('t', 'w', 'o', 's'))
_parentTrack->sampleSize = (entry->_bitsPerSample / 8) * entry->_channels;
-
+
// Initialize our edit parser too
_curEdit = 0;
enterNewEdit(Timestamp());
@@ -395,9 +395,9 @@ AudioStream *QuickTimeAudioDecoder::QuickTimeAudioTrack::readAudioChunk(uint chu
}
void QuickTimeAudioDecoder::QuickTimeAudioTrack::skipSamples(const Timestamp &length, AudioStream *stream) {
- uint32 sampleCount = length.convertToFramerate(getRate()).totalNumberOfFrames();
+ int32 sampleCount = length.convertToFramerate(getRate()).totalNumberOfFrames();
- if (sampleCount == 0)
+ if (sampleCount <= 0)
return;
if (isStereo())
@@ -426,7 +426,7 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::enterNewEdit(const Timestamp &p
// If we're at the end of the edit list, there's nothing else for us to do here
if (allDataRead())
return;
-
+
// For an empty edit, we may need to adjust the start time
if (_parentTrack->editList[_curEdit].mediaTime == -1) {
// Just invalidate the current media position (and make sure the scale
diff --git a/audio/decoders/quicktime.h b/audio/decoders/quicktime.h
index 4dd1a57710..4c0b93488e 100644
--- a/audio/decoders/quicktime.h
+++ b/audio/decoders/quicktime.h
@@ -25,6 +25,7 @@
* Sound decoder used in engines:
* - groovie
* - mohawk
+ * - pegasus
* - sci
*/
diff --git a/audio/decoders/quicktime_intern.h b/audio/decoders/quicktime_intern.h
index efc97cbd13..f1ab037d89 100644
--- a/audio/decoders/quicktime_intern.h
+++ b/audio/decoders/quicktime_intern.h
@@ -88,7 +88,7 @@ protected:
private:
QuickTimeAudioDecoder *_decoder;
- Track *_parentTrack;
+ Track *_parentTrack;
QueuingAudioStream *_queue;
uint _curChunk;
Timestamp _curMediaPos, _skipSamples;
@@ -115,7 +115,7 @@ protected:
~AudioSampleDesc();
bool isAudioCodecSupported() const;
-
+
AudioStream *createAudioStream(Common::SeekableReadStream *stream) const;
void initCodec();
diff --git a/audio/mididrv.cpp b/audio/mididrv.cpp
index 0518915e81..dea07a739b 100644
--- a/audio/mididrv.cpp
+++ b/audio/mididrv.cpp
@@ -240,7 +240,7 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
devStr = ConfMan.hasKey("gm_device") ? ConfMan.get("gm_device") : Common::String("null");
else
devStr = "auto";
-
+
// Default to Null device here, since we also register a default null setting for
// the MT32 or GM device in the config manager.
hdl = getDeviceHandle(devStr.empty() ? Common::String("null") : devStr);
diff --git a/audio/midiparser.cpp b/audio/midiparser.cpp
index 943a6067a4..eec32c05d1 100644
--- a/audio/midiparser.cpp
+++ b/audio/midiparser.cpp
@@ -32,24 +32,24 @@
//////////////////////////////////////////////////
MidiParser::MidiParser() :
-_hanging_notes_count(0),
+_hangingNotesCount(0),
_driver(0),
-_timer_rate(0x4A0000),
+_timerRate(0x4A0000),
_ppqn(96),
_tempo(500000),
-_psec_per_tick(5208), // 500000 / 96
+_psecPerTick(5208), // 500000 / 96
_autoLoop(false),
_smartJump(false),
_centerPitchWheelOnUnload(false),
_sendSustainOffOnNotesOff(false),
-_num_tracks(0),
-_active_track(255),
-_abort_parse(0) {
- memset(_active_notes, 0, sizeof(_active_notes));
- _next_event.start = NULL;
- _next_event.delta = 0;
- _next_event.event = 0;
- _next_event.length = 0;
+_numTracks(0),
+_activeTrack(255),
+_abortParse(0) {
+ memset(_activeNotes, 0, sizeof(_activeNotes));
+ _nextEvent.start = NULL;
+ _nextEvent.delta = 0;
+ _nextEvent.event = 0;
+ _nextEvent.length = 0;
}
void MidiParser::property(int prop, int value) {
@@ -76,7 +76,7 @@ void MidiParser::sendToDriver(uint32 b) {
void MidiParser::setTempo(uint32 tempo) {
_tempo = tempo;
if (_ppqn)
- _psec_per_tick = (tempo + (_ppqn >> 2)) / _ppqn;
+ _psecPerTick = (tempo + (_ppqn >> 2)) / _ppqn;
}
// This is the conventional (i.e. SMF) variable length quantity
@@ -100,44 +100,44 @@ void MidiParser::activeNote(byte channel, byte note, bool active) {
return;
if (active)
- _active_notes[note] |= (1 << channel);
+ _activeNotes[note] |= (1 << channel);
else
- _active_notes[note] &= ~(1 << channel);
+ _activeNotes[note] &= ~(1 << channel);
// See if there are hanging notes that we can cancel
- NoteTimer *ptr = _hanging_notes;
+ NoteTimer *ptr = _hangingNotes;
int i;
- for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) {
- if (ptr->channel == channel && ptr->note == note && ptr->time_left) {
- ptr->time_left = 0;
- --_hanging_notes_count;
+ for (i = ARRAYSIZE(_hangingNotes); i; --i, ++ptr) {
+ if (ptr->channel == channel && ptr->note == note && ptr->timeLeft) {
+ ptr->timeLeft = 0;
+ --_hangingNotesCount;
break;
}
}
}
-void MidiParser::hangingNote(byte channel, byte note, uint32 time_left, bool recycle) {
+void MidiParser::hangingNote(byte channel, byte note, uint32 timeLeft, bool recycle) {
NoteTimer *best = 0;
- NoteTimer *ptr = _hanging_notes;
+ NoteTimer *ptr = _hangingNotes;
int i;
- if (_hanging_notes_count >= ARRAYSIZE(_hanging_notes)) {
+ if (_hangingNotesCount >= ARRAYSIZE(_hangingNotes)) {
warning("MidiParser::hangingNote(): Exceeded polyphony");
return;
}
- for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) {
+ for (i = ARRAYSIZE(_hangingNotes); i; --i, ++ptr) {
if (ptr->channel == channel && ptr->note == note) {
- if (ptr->time_left && ptr->time_left < time_left && recycle)
+ if (ptr->timeLeft && ptr->timeLeft < timeLeft && recycle)
return;
best = ptr;
- if (ptr->time_left) {
+ if (ptr->timeLeft) {
if (recycle)
sendToDriver(0x80 | channel, note, 0);
- --_hanging_notes_count;
+ --_hangingNotesCount;
}
break;
- } else if (!best && ptr->time_left == 0) {
+ } else if (!best && ptr->timeLeft == 0) {
best = ptr;
}
}
@@ -146,14 +146,14 @@ void MidiParser::hangingNote(byte channel, byte note, uint32 time_left, bool rec
// length, if the note should be turned on and off in
// the same iteration. For now just set it to 1 and
// we'll turn it off in the next cycle.
- if (!time_left || time_left & 0x80000000)
- time_left = 1;
+ if (!timeLeft || timeLeft & 0x80000000)
+ timeLeft = 1;
if (best) {
best->channel = channel;
best->note = note;
- best->time_left = time_left;
- ++_hanging_notes_count;
+ best->timeLeft = timeLeft;
+ ++_hangingNotesCount;
} else {
// We checked this up top. We should never get here!
warning("MidiParser::hangingNote(): Internal error");
@@ -161,45 +161,45 @@ void MidiParser::hangingNote(byte channel, byte note, uint32 time_left, bool rec
}
void MidiParser::onTimer() {
- uint32 end_time;
- uint32 event_time;
+ uint32 endTime;
+ uint32 eventTime;
- if (!_position._play_pos || !_driver)
+ if (!_position._playPos || !_driver)
return;
- _abort_parse = false;
- end_time = _position._play_time + _timer_rate;
+ _abortParse = false;
+ endTime = _position._playTime + _timerRate;
// Scan our hanging notes for any
// that should be turned off.
- if (_hanging_notes_count) {
- NoteTimer *ptr = &_hanging_notes[0];
+ if (_hangingNotesCount) {
+ NoteTimer *ptr = &_hangingNotes[0];
int i;
- for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) {
- if (ptr->time_left) {
- if (ptr->time_left <= _timer_rate) {
+ for (i = ARRAYSIZE(_hangingNotes); i; --i, ++ptr) {
+ if (ptr->timeLeft) {
+ if (ptr->timeLeft <= _timerRate) {
sendToDriver(0x80 | ptr->channel, ptr->note, 0);
- ptr->time_left = 0;
- --_hanging_notes_count;
+ ptr->timeLeft = 0;
+ --_hangingNotesCount;
} else {
- ptr->time_left -= _timer_rate;
+ ptr->timeLeft -= _timerRate;
}
}
}
}
- while (!_abort_parse) {
- EventInfo &info = _next_event;
+ while (!_abortParse) {
+ EventInfo &info = _nextEvent;
- event_time = _position._last_event_time + info.delta * _psec_per_tick;
- if (event_time > end_time)
+ eventTime = _position._lastEventTime + info.delta * _psecPerTick;
+ if (eventTime > endTime)
break;
// Process the next info.
- _position._last_event_tick += info.delta;
+ _position._lastEventTick += info.delta;
if (info.event < 0x80) {
warning("Bad command or running status %02X", info.event);
- _position._play_pos = 0;
+ _position._playPos = 0;
return;
}
@@ -217,7 +217,7 @@ void MidiParser::onTimer() {
// as well as sending it to the output device.
if (_autoLoop) {
jumpToTick(0);
- parseNextEvent(_next_event);
+ parseNextEvent(_nextEvent);
} else {
stopPlaying();
_driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length);
@@ -234,7 +234,7 @@ void MidiParser::onTimer() {
activeNote(info.channel(), info.basic.param1, false);
} else if (info.command() == 0x9) {
if (info.length > 0)
- hangingNote(info.channel(), info.basic.param1, info.length * _psec_per_tick - (end_time - event_time));
+ hangingNote(info.channel(), info.basic.param1, info.length * _psecPerTick - (endTime - eventTime));
else
activeNote(info.channel(), info.basic.param1, true);
}
@@ -242,15 +242,15 @@ void MidiParser::onTimer() {
}
- if (!_abort_parse) {
- _position._last_event_time = event_time;
- parseNextEvent(_next_event);
+ if (!_abortParse) {
+ _position._lastEventTime = eventTime;
+ parseNextEvent(_nextEvent);
}
}
- if (!_abort_parse) {
- _position._play_time = end_time;
- _position._play_tick = (_position._play_time - _position._last_event_time) / _psec_per_tick + _position._last_event_tick;
+ if (!_abortParse) {
+ _position._playTime = endTime;
+ _position._playTick = (_position._playTime - _position._lastEventTime) / _psecPerTick + _position._lastEventTick;
}
}
@@ -263,20 +263,20 @@ void MidiParser::allNotesOff() {
// Turn off all active notes
for (i = 0; i < 128; ++i) {
for (j = 0; j < 16; ++j) {
- if (_active_notes[i] & (1 << j)) {
+ if (_activeNotes[i] & (1 << j)) {
sendToDriver(0x80 | j, i, 0);
}
}
}
// Turn off all hanging notes
- for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) {
- if (_hanging_notes[i].time_left) {
- sendToDriver(0x80 | _hanging_notes[i].channel, _hanging_notes[i].note, 0);
- _hanging_notes[i].time_left = 0;
+ for (i = 0; i < ARRAYSIZE(_hangingNotes); i++) {
+ if (_hangingNotes[i].timeLeft) {
+ sendToDriver(0x80 | _hangingNotes[i].channel, _hangingNotes[i].note, 0);
+ _hangingNotes[i].timeLeft = 0;
}
}
- _hanging_notes_count = 0;
+ _hangingNotesCount = 0;
// To be sure, send an "All Note Off" event (but not all MIDI devices
// support this...).
@@ -287,7 +287,7 @@ void MidiParser::allNotesOff() {
sendToDriver(0xB0 | i, 0x40, 0); // Also send a sustain off event (bug #3116608)
}
- memset(_active_notes, 0, sizeof(_active_notes));
+ memset(_activeNotes, 0, sizeof(_activeNotes));
}
void MidiParser::resetTracking() {
@@ -295,7 +295,7 @@ void MidiParser::resetTracking() {
}
bool MidiParser::setTrack(int track) {
- if (track < 0 || track >= _num_tracks)
+ if (track < 0 || track >= _numTracks)
return false;
// We allow restarting the track via setTrack when
// it isn't playing anymore. This allows us to reuse
@@ -308,7 +308,7 @@ bool MidiParser::setTrack(int track) {
// TODO: Check if any engine has problem with this
// handling, if so we need to find a better way to handle
// track restarts. (KYRA relies on this working)
- else if (track == _active_track && isPlaying())
+ else if (track == _activeTrack && isPlaying())
return true;
if (_smartJump)
@@ -317,10 +317,10 @@ bool MidiParser::setTrack(int track) {
allNotesOff();
resetTracking();
- memset(_active_notes, 0, sizeof(_active_notes));
- _active_track = track;
- _position._play_pos = _tracks[track];
- parseNextEvent(_next_event);
+ memset(_activeNotes, 0, sizeof(_activeNotes));
+ _activeTrack = track;
+ _position._playPos = _tracks[track];
+ parseNextEvent(_nextEvent);
return true;
}
@@ -332,29 +332,29 @@ void MidiParser::stopPlaying() {
void MidiParser::hangAllActiveNotes() {
// Search for note off events until we have
// accounted for every active note.
- uint16 temp_active[128];
- memcpy(temp_active, _active_notes, sizeof (temp_active));
+ uint16 tempActive[128];
+ memcpy(tempActive, _activeNotes, sizeof (tempActive));
- uint32 advance_tick = _position._last_event_tick;
+ uint32 advanceTick = _position._lastEventTick;
while (true) {
int i;
for (i = 0; i < 128; ++i)
- if (temp_active[i] != 0)
+ if (tempActive[i] != 0)
break;
if (i == 128)
break;
- parseNextEvent(_next_event);
- advance_tick += _next_event.delta;
- if (_next_event.command() == 0x8) {
- if (temp_active[_next_event.basic.param1] & (1 << _next_event.channel())) {
- hangingNote(_next_event.channel(), _next_event.basic.param1, (advance_tick - _position._last_event_tick) * _psec_per_tick, false);
- temp_active[_next_event.basic.param1] &= ~(1 << _next_event.channel());
+ parseNextEvent(_nextEvent);
+ advanceTick += _nextEvent.delta;
+ if (_nextEvent.command() == 0x8) {
+ if (tempActive[_nextEvent.basic.param1] & (1 << _nextEvent.channel())) {
+ hangingNote(_nextEvent.channel(), _nextEvent.basic.param1, (advanceTick - _position._lastEventTick) * _psecPerTick, false);
+ tempActive[_nextEvent.basic.param1] &= ~(1 << _nextEvent.channel());
}
- } else if (_next_event.event == 0xFF && _next_event.ext.type == 0x2F) {
+ } else if (_nextEvent.event == 0xFF && _nextEvent.ext.type == 0x2F) {
// warning("MidiParser::hangAllActiveNotes(): Hit End of Track with active notes left");
for (i = 0; i < 128; ++i) {
for (int j = 0; j < 16; ++j) {
- if (temp_active[i] & (1 << j)) {
+ if (tempActive[i] & (1 << j)) {
activeNote(j, i, false);
sendToDriver(0x80 | j, i, 0);
}
@@ -366,33 +366,33 @@ void MidiParser::hangAllActiveNotes() {
}
bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool dontSendNoteOn) {
- if (_active_track >= _num_tracks)
+ if (_activeTrack >= _numTracks)
return false;
Tracker currentPos(_position);
- EventInfo currentEvent(_next_event);
+ EventInfo currentEvent(_nextEvent);
resetTracking();
- _position._play_pos = _tracks[_active_track];
- parseNextEvent(_next_event);
+ _position._playPos = _tracks[_activeTrack];
+ parseNextEvent(_nextEvent);
if (tick > 0) {
while (true) {
- EventInfo &info = _next_event;
- if (_position._last_event_tick + info.delta >= tick) {
- _position._play_time += (tick - _position._last_event_tick) * _psec_per_tick;
- _position._play_tick = tick;
+ EventInfo &info = _nextEvent;
+ if (_position._lastEventTick + info.delta >= tick) {
+ _position._playTime += (tick - _position._lastEventTick) * _psecPerTick;
+ _position._playTick = tick;
break;
}
- _position._last_event_tick += info.delta;
- _position._last_event_time += info.delta * _psec_per_tick;
- _position._play_tick = _position._last_event_tick;
- _position._play_time = _position._last_event_time;
+ _position._lastEventTick += info.delta;
+ _position._lastEventTime += info.delta * _psecPerTick;
+ _position._playTick = _position._lastEventTick;
+ _position._playTime = _position._lastEventTime;
if (info.event == 0xFF) {
if (info.ext.type == 0x2F) { // End of track
_position = currentPos;
- _next_event = currentEvent;
+ _nextEvent = currentEvent;
return false;
} else {
if (info.ext.type == 0x51 && info.length >= 3) // Tempo
@@ -419,36 +419,36 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool d
}
}
- parseNextEvent(_next_event);
+ parseNextEvent(_nextEvent);
}
}
if (stopNotes) {
- if (!_smartJump || !currentPos._play_pos) {
+ if (!_smartJump || !currentPos._playPos) {
allNotesOff();
} else {
- EventInfo targetEvent(_next_event);
+ EventInfo targetEvent(_nextEvent);
Tracker targetPosition(_position);
_position = currentPos;
- _next_event = currentEvent;
+ _nextEvent = currentEvent;
hangAllActiveNotes();
- _next_event = targetEvent;
+ _nextEvent = targetEvent;
_position = targetPosition;
}
}
- _abort_parse = true;
+ _abortParse = true;
return true;
}
void MidiParser::unloadMusic() {
resetTracking();
allNotesOff();
- _num_tracks = 0;
- _active_track = 255;
- _abort_parse = true;
+ _numTracks = 0;
+ _activeTrack = 255;
+ _abortParse = true;
if (_centerPitchWheelOnUnload) {
// Center the pitch wheels in preparation for the next piece of
diff --git a/audio/midiparser.h b/audio/midiparser.h
index c935969e72..a4dbf174e1 100644
--- a/audio/midiparser.h
+++ b/audio/midiparser.h
@@ -49,33 +49,33 @@ class MidiDriver_BASE;
* each Tracker location.
*/
struct Tracker {
- byte * _play_pos; ///< A pointer to the next event to be parsed
- uint32 _play_time; ///< Current time in microseconds; may be in between event times
- uint32 _play_tick; ///< Current MIDI tick; may be in between event ticks
- uint32 _last_event_time; ///< The time, in microseconds, of the last event that was parsed
- uint32 _last_event_tick; ///< The tick at which the last parsed event occurs
- byte _running_status; ///< Cached MIDI command, for MIDI streams that rely on implied event codes
+ byte * _playPos; ///< A pointer to the next event to be parsed
+ uint32 _playTime; ///< Current time in microseconds; may be in between event times
+ uint32 _playTick; ///< Current MIDI tick; may be in between event ticks
+ uint32 _lastEventTime; ///< The time, in microseconds, of the last event that was parsed
+ uint32 _lastEventTick; ///< The tick at which the last parsed event occurs
+ byte _runningStatus; ///< Cached MIDI command, for MIDI streams that rely on implied event codes
Tracker() { clear(); }
/// Copy constructor for each duplication of Tracker information.
Tracker(const Tracker &copy) :
- _play_pos(copy._play_pos),
- _play_time(copy._play_time),
- _play_tick(copy._play_tick),
- _last_event_time(copy._last_event_time),
- _last_event_tick(copy._last_event_tick),
- _running_status(copy._running_status)
+ _playPos(copy._playPos),
+ _playTime(copy._playTime),
+ _playTick(copy._playTick),
+ _lastEventTime(copy._lastEventTime),
+ _lastEventTick(copy._lastEventTick),
+ _runningStatus(copy._runningStatus)
{ }
/// Clears all data; used by the constructor for initialization.
void clear() {
- _play_pos = 0;
- _play_time = 0;
- _play_tick = 0;
- _last_event_time = 0;
- _last_event_tick = 0;
- _running_status = 0;
+ _playPos = 0;
+ _playTime = 0;
+ _playTick = 0;
+ _lastEventTime = 0;
+ _lastEventTick = 0;
+ _runningStatus = 0;
}
};
@@ -119,8 +119,8 @@ struct EventInfo {
struct NoteTimer {
byte channel; ///< The MIDI channel on which the note was played
byte note; ///< The note number for the active note
- uint32 time_left; ///< The time, in microseconds, remaining before the note should be turned off
- NoteTimer() : channel(0), note(0), time_left(0) {}
+ uint32 timeLeft; ///< The time, in microseconds, remaining before the note should be turned off
+ NoteTimer() : channel(0), note(0), timeLeft(0) {}
};
@@ -264,29 +264,29 @@ struct NoteTimer {
*/
class MidiParser {
protected:
- uint16 _active_notes[128]; ///< Each uint16 is a bit mask for channels that have that note on.
- NoteTimer _hanging_notes[32]; ///< Maintains expiration info for up to 32 notes.
+ uint16 _activeNotes[128]; ///< Each uint16 is a bit mask for channels that have that note on.
+ NoteTimer _hangingNotes[32]; ///< Maintains expiration info for up to 32 notes.
///< Used for "Smart Jump" and MIDI formats that do not include explicit Note Off events.
- byte _hanging_notes_count; ///< Count of hanging notes, used to optimize expiration.
+ byte _hangingNotesCount; ///< Count of hanging notes, used to optimize expiration.
MidiDriver_BASE *_driver; ///< The device to which all events will be transmitted.
- uint32 _timer_rate; ///< The time in microseconds between onTimer() calls. Obtained from the MidiDriver.
+ uint32 _timerRate; ///< The time in microseconds between onTimer() calls. Obtained from the MidiDriver.
uint32 _ppqn; ///< Pulses Per Quarter Note. (We refer to "pulses" as "ticks".)
uint32 _tempo; ///< Microseconds per quarter note.
- uint32 _psec_per_tick; ///< Microseconds per tick (_tempo / _ppqn).
+ uint32 _psecPerTick; ///< Microseconds per tick (_tempo / _ppqn).
bool _autoLoop; ///< For lightweight clients that don't provide their own flow control.
bool _smartJump; ///< Support smart expiration of hanging notes when jumping
bool _centerPitchWheelOnUnload; ///< Center the pitch wheels when unloading a song
bool _sendSustainOffOnNotesOff; ///< Send a sustain off on a notes off event, stopping hanging notes
byte *_tracks[120]; ///< Multi-track MIDI formats are supported, up to 120 tracks.
- byte _num_tracks; ///< Count of total tracks for multi-track MIDI formats. 1 for single-track formats.
- byte _active_track; ///< Keeps track of the currently active track, in multi-track formats.
+ byte _numTracks; ///< Count of total tracks for multi-track MIDI formats. 1 for single-track formats.
+ byte _activeTrack; ///< Keeps track of the currently active track, in multi-track formats.
Tracker _position; ///< The current time/position in the active track.
- EventInfo _next_event; ///< The next event to transmit. Events are preparsed
+ EventInfo _nextEvent; ///< The next event to transmit. Events are preparsed
///< so each event is parsed only once; this permits
///< simulated events in certain formats.
- bool _abort_parse; ///< If a jump or other operation interrupts parsing, flag to abort.
+ bool _abortParse; ///< If a jump or other operation interrupts parsing, flag to abort.
protected:
static uint32 readVLQ(byte * &data);
@@ -295,7 +295,7 @@ protected:
virtual void parseNextEvent(EventInfo &info) = 0;
void activeNote(byte channel, byte note, bool active);
- void hangingNote(byte channel, byte note, uint32 ticks_left, bool recycle = true);
+ void hangingNote(byte channel, byte note, uint32 ticksLeft, bool recycle = true);
void hangAllActiveNotes();
virtual void sendToDriver(uint32 b);
@@ -377,18 +377,18 @@ public:
virtual void property(int prop, int value);
void setMidiDriver(MidiDriver_BASE *driver) { _driver = driver; }
- void setTimerRate(uint32 rate) { _timer_rate = rate; }
+ void setTimerRate(uint32 rate) { _timerRate = rate; }
void setTempo(uint32 tempo);
void onTimer();
- bool isPlaying() const { return (_position._play_pos != 0); }
+ bool isPlaying() const { return (_position._playPos != 0); }
void stopPlaying();
bool setTrack(int track);
bool jumpToTick(uint32 tick, bool fireEvents = false, bool stopNotes = true, bool dontSendNoteOn = false);
uint32 getPPQN() { return _ppqn; }
- virtual uint32 getTick() { return _position._play_tick; }
+ virtual uint32 getTick() { return _position._playTick; }
static void defaultXMidiCallback(byte eventData, void *refCon);
diff --git a/audio/midiparser_smf.cpp b/audio/midiparser_smf.cpp
index e883471b54..4b0913cbfe 100644
--- a/audio/midiparser_smf.cpp
+++ b/audio/midiparser_smf.cpp
@@ -45,8 +45,8 @@ public:
};
-static const byte command_lengths[8] = { 3, 3, 3, 3, 2, 2, 3, 0 };
-static const byte special_lengths[16] = { 0, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 };
+static const byte commandLengths[8] = { 3, 3, 3, 3, 2, 2, 3, 0 };
+static const byte specialLengths[16] = { 0, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 };
MidiParser_SMF::~MidiParser_SMF() {
free(_buffer);
@@ -62,8 +62,8 @@ void MidiParser_SMF::property(int prop, int value) {
}
void MidiParser_SMF::parseNextEvent(EventInfo &info) {
- info.start = _position._play_pos;
- info.delta = readVLQ(_position._play_pos);
+ info.start = _position._playPos;
+ info.delta = readVLQ(_position._playPos);
// Process the next info. If mpMalformedPitchBends
// was set, we must skip over any pitch bend events
@@ -71,19 +71,19 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) {
// real pitch bend events, they're just two-byte
// prefixes before the real info.
do {
- if ((_position._play_pos[0] & 0xF0) >= 0x80)
- info.event = *(_position._play_pos++);
+ if ((_position._playPos[0] & 0xF0) >= 0x80)
+ info.event = *(_position._playPos++);
else
- info.event = _position._running_status;
- } while (_malformedPitchBends && (info.event & 0xF0) == 0xE0 && _position._play_pos++);
+ info.event = _position._runningStatus;
+ } while (_malformedPitchBends && (info.event & 0xF0) == 0xE0 && _position._playPos++);
if (info.event < 0x80)
return;
- _position._running_status = info.event;
+ _position._runningStatus = info.event;
switch (info.command()) {
case 0x9: // Note On
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
if (info.basic.param2 == 0)
info.event = info.channel() | 0x80;
info.length = 0;
@@ -91,7 +91,7 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) {
case 0xC:
case 0xD:
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
@@ -99,20 +99,20 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) {
case 0xA:
case 0xB:
case 0xE:
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
info.length = 0;
break;
case 0xF: // System Common, Meta or SysEx event
switch (info.event & 0x0F) {
case 0x2: // Song Position Pointer
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
break;
case 0x3: // Song Select
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
@@ -126,16 +126,16 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) {
break;
case 0x0: // SysEx
- info.length = readVLQ(_position._play_pos);
- info.ext.data = _position._play_pos;
- _position._play_pos += info.length;
+ info.length = readVLQ(_position._playPos);
+ info.ext.data = _position._playPos;
+ _position._playPos += info.length;
break;
case 0xF: // META event
- info.ext.type = *(_position._play_pos++);
- info.length = readVLQ(_position._play_pos);
- info.ext.data = _position._play_pos;
- _position._play_pos += info.length;
+ info.ext.type = *(_position._playPos++);
+ info.length = readVLQ(_position._playPos);
+ info.ext.data = _position._playPos;
+ _position._playPos += info.length;
break;
default:
@@ -146,8 +146,8 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) {
bool MidiParser_SMF::loadMusic(byte *data, uint32 size) {
uint32 len;
- byte midi_type;
- uint32 total_size;
+ byte midiType;
+ uint32 totalSize;
bool isGMF;
unloadMusic();
@@ -171,10 +171,10 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) {
// Verify that this MIDI either is a Type 2
// or has only 1 track. We do not support
// multitrack Type 1 files.
- _num_tracks = pos[2] << 8 | pos[3];
- midi_type = pos[1];
- if (midi_type > 2 /*|| (midi_type < 2 && _num_tracks > 1)*/) {
- warning("No support for a Type %d MIDI with %d tracks", (int)midi_type, (int)_num_tracks);
+ _numTracks = pos[2] << 8 | pos[3];
+ midiType = pos[1];
+ if (midiType > 2 /*|| (midiType < 2 && _numTracks > 1)*/) {
+ warning("No support for a Type %d MIDI with %d tracks", (int)midiType, (int)_numTracks);
return false;
}
_ppqn = pos[4] << 8 | pos[5];
@@ -183,8 +183,8 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) {
// Older GMD/MUS file with no header info.
// Assume 1 track, 192 PPQN, and no MTrk headers.
isGMF = true;
- midi_type = 0;
- _num_tracks = 1;
+ midiType = 0;
+ _numTracks = 1;
_ppqn = 192;
pos += 7; // 'GMD\x1' + 3 bytes of useless (translate: unknown) information
} else {
@@ -193,14 +193,14 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) {
}
// Now we identify and store the location for each track.
- if (_num_tracks > ARRAYSIZE(_tracks)) {
- warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_num_tracks);
+ if (_numTracks > ARRAYSIZE(_tracks)) {
+ warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_numTracks);
return false;
}
- total_size = 0;
- int tracks_read = 0;
- while (tracks_read < _num_tracks) {
+ totalSize = 0;
+ int tracksRead = 0;
+ while (tracksRead < _numTracks) {
if (memcmp(pos, "MTrk", 4) && !isGMF) {
warning("Position: %p ('%c')", pos, *pos);
warning("Hit invalid block '%c%c%c%c' while scanning for track locations", pos[0], pos[1], pos[2], pos[3]);
@@ -208,11 +208,11 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) {
}
// If needed, skip the MTrk and length bytes
- _tracks[tracks_read] = pos + (isGMF ? 0 : 8);
+ _tracks[tracksRead] = pos + (isGMF ? 0 : 8);
if (!isGMF) {
pos += 4;
len = read4high(pos);
- total_size += len;
+ totalSize += len;
pos += len;
} else {
// An SMF End of Track meta event must be placed
@@ -222,7 +222,7 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) {
data[size++] = 0x00;
data[size++] = 0x00;
}
- ++tracks_read;
+ ++tracksRead;
}
// If this is a Type 1 MIDI, we need to now compress
@@ -230,13 +230,13 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) {
free(_buffer);
_buffer = 0;
- if (midi_type == 1) {
+ if (midiType == 1) {
// FIXME: Doubled the buffer size to prevent crashes with the
// Inherit the Earth MIDIs. Jamieson630 said something about a
// better fix, but this will have to do in the meantime.
_buffer = (byte *)malloc(size * 2);
compressToType0();
- _num_tracks = 1;
+ _numTracks = 1;
_tracks[0] = _buffer;
}
@@ -253,48 +253,48 @@ void MidiParser_SMF::compressToType0() {
// We assume that _buffer has been allocated
// to sufficient size for this operation.
- // using 0xFF since it could write track_pos[0 to _num_tracks] here
+ // using 0xFF since it could write trackPos[0 to _numTracks] here
// this would cause some illegal writes and could lead to segfaults
// (it crashed for some midis for me, they're not used in any game
// scummvm supports though). *Maybe* handle this in another way,
// it's at the moment only to be sure, that nothing goes wrong.
- byte *track_pos[0xFF];
- byte running_status[0xFF];
- uint32 track_timer[0xFF];
+ byte *trackPos[0xFF];
+ byte runningStatus[0xFF];
+ uint32 trackTimer[0xFF];
uint32 delta;
int i;
- for (i = 0; i < _num_tracks; ++i) {
- running_status[i] = 0;
- track_pos[i] = _tracks[i];
- track_timer[i] = readVLQ(track_pos[i]);
- running_status[i] = 0;
+ for (i = 0; i < _numTracks; ++i) {
+ runningStatus[i] = 0;
+ trackPos[i] = _tracks[i];
+ trackTimer[i] = readVLQ(trackPos[i]);
+ runningStatus[i] = 0;
}
- int best_i;
+ int bestTrack;
uint32 length;
byte *output = _buffer;
byte *pos, *pos2;
byte event;
- uint32 copy_bytes;
+ uint32 copyBytes;
bool write;
- byte active_tracks = (byte)_num_tracks;
+ byte activeTracks = (byte)_numTracks;
- while (active_tracks) {
+ while (activeTracks) {
write = true;
- best_i = 255;
- for (i = 0; i < _num_tracks; ++i) {
- if (track_pos[i] && (best_i == 255 || track_timer[i] < track_timer[best_i]))
- best_i = i;
+ bestTrack = 255;
+ for (i = 0; i < _numTracks; ++i) {
+ if (trackPos[i] && (bestTrack == 255 || trackTimer[i] < trackTimer[bestTrack]))
+ bestTrack = i;
}
- if (best_i == 255) {
+ if (bestTrack == 255) {
warning("Premature end of tracks");
break;
}
// Initial VLQ delta computation
delta = 0;
- length = track_timer[best_i];
+ length = trackTimer[bestTrack];
for (i = 0; length; ++i) {
delta = (delta << 8) | (length & 0x7F) | (i ? 0x80 : 0);
length >>= 7;
@@ -302,55 +302,55 @@ void MidiParser_SMF::compressToType0() {
// Process MIDI event.
bool implicitEvent = false;
- copy_bytes = 0;
- pos = track_pos[best_i];
+ copyBytes = 0;
+ pos = trackPos[bestTrack];
do {
event = *(pos++);
if (event < 0x80) {
- event = running_status[best_i];
+ event = runningStatus[bestTrack];
implicitEvent = true;
}
} while (_malformedPitchBends && (event & 0xF0) == 0xE0 && pos++);
- running_status[best_i] = event;
+ runningStatus[bestTrack] = event;
- if (command_lengths[(event >> 4) - 8] > 0) {
- copy_bytes = command_lengths[(event >> 4) - 8];
- } else if (special_lengths[(event & 0x0F)] > 0) {
- copy_bytes = special_lengths[(event & 0x0F)];
+ if (commandLengths[(event >> 4) - 8] > 0) {
+ copyBytes = commandLengths[(event >> 4) - 8];
+ } else if (specialLengths[(event & 0x0F)] > 0) {
+ copyBytes = specialLengths[(event & 0x0F)];
} else if (event == 0xF0) {
// SysEx
pos2 = pos;
length = readVLQ(pos);
- copy_bytes = 1 + (pos - pos2) + length;
+ copyBytes = 1 + (pos - pos2) + length;
} else if (event == 0xFF) {
// META
event = *(pos++);
- if (event == 0x2F && active_tracks > 1) {
- track_pos[best_i] = 0;
+ if (event == 0x2F && activeTracks > 1) {
+ trackPos[bestTrack] = 0;
write = false;
} else {
pos2 = pos;
length = readVLQ(pos);
- copy_bytes = 2 + (pos - pos2) + length;
+ copyBytes = 2 + (pos - pos2) + length;
}
if (event == 0x2F)
- --active_tracks;
+ --activeTracks;
} else {
warning("Bad MIDI command %02X", (int)event);
- track_pos[best_i] = 0;
+ trackPos[bestTrack] = 0;
}
// Update all tracks' deltas
if (write) {
- for (i = 0; i < _num_tracks; ++i) {
- if (track_pos[i] && i != best_i)
- track_timer[i] -= track_timer[best_i];
+ for (i = 0; i < _numTracks; ++i) {
+ if (trackPos[i] && i != bestTrack)
+ trackTimer[i] -= trackTimer[bestTrack];
}
}
- if (track_pos[best_i]) {
+ if (trackPos[bestTrack]) {
if (write) {
- track_timer[best_i] = 0;
+ trackTimer[bestTrack] = 0;
// Write VLQ delta
while (delta & 0x80) {
@@ -361,17 +361,17 @@ void MidiParser_SMF::compressToType0() {
// Write MIDI data
if (!implicitEvent)
- ++track_pos[best_i];
- --copy_bytes;
- *output++ = running_status[best_i];
- memcpy(output, track_pos[best_i], copy_bytes);
- output += copy_bytes;
+ ++trackPos[bestTrack];
+ --copyBytes;
+ *output++ = runningStatus[bestTrack];
+ memcpy(output, trackPos[bestTrack], copyBytes);
+ output += copyBytes;
}
// Fetch new VLQ delta for winning track
- track_pos[best_i] += copy_bytes;
- if (active_tracks)
- track_timer[best_i] += readVLQ(track_pos[best_i]);
+ trackPos[bestTrack] += copyBytes;
+ if (activeTracks)
+ trackTimer[bestTrack] += readVLQ(trackPos[bestTrack]);
}
}
diff --git a/audio/midiparser_xmidi.cpp b/audio/midiparser_xmidi.cpp
index 85491faaf8..11690b0214 100644
--- a/audio/midiparser_xmidi.cpp
+++ b/audio/midiparser_xmidi.cpp
@@ -32,9 +32,6 @@
*/
class MidiParser_XMIDI : public MidiParser {
protected:
- NoteTimer _notes_cache[32];
- uint32 _inserted_delta; // Track simulated deltas for note-off events
-
struct Loop {
byte *pos;
byte repeat;
@@ -48,11 +45,10 @@ protected:
protected:
uint32 readVLQ2(byte * &data);
- void resetTracking();
void parseNextEvent(EventInfo &info);
public:
- MidiParser_XMIDI(XMidiCallbackProc proc, void *data) : _inserted_delta(0), _callbackProc(proc), _callbackData(data) {}
+ MidiParser_XMIDI(XMidiCallbackProc proc, void *data) : _callbackProc(proc), _callbackData(data) {}
~MidiParser_XMIDI() { }
bool loadMusic(byte *data, uint32 size);
@@ -69,17 +65,16 @@ uint32 MidiParser_XMIDI::readVLQ2(byte * &pos) {
}
void MidiParser_XMIDI::parseNextEvent(EventInfo &info) {
- info.start = _position._play_pos;
- info.delta = readVLQ2(_position._play_pos) - _inserted_delta;
+ info.start = _position._playPos;
+ info.delta = readVLQ2(_position._playPos);
// Process the next event.
- _inserted_delta = 0;
- info.event = *(_position._play_pos++);
+ info.event = *(_position._playPos++);
switch (info.event >> 4) {
case 0x9: // Note On
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
- info.length = readVLQ(_position._play_pos);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
+ info.length = readVLQ(_position._playPos);
if (info.basic.param2 == 0) {
info.event = info.channel() | 0x80;
info.length = 0;
@@ -88,20 +83,20 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) {
case 0xC:
case 0xD:
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
case 0x8:
case 0xA:
case 0xE:
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
break;
case 0xB:
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
// This isn't a full XMIDI implementation, but it should
// hopefully be "good enough" for most things.
@@ -109,7 +104,7 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) {
switch (info.basic.param1) {
// Simplified XMIDI looping.
case 0x74: { // XMIDI_CONTROLLER_FOR_LOOP
- byte *pos = _position._play_pos;
+ byte *pos = _position._playPos;
if (_loopCount < ARRAYSIZE(_loop) - 1)
_loopCount++;
else
@@ -131,9 +126,9 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) {
if (--_loop[_loopCount].repeat == 0)
_loopCount--;
else
- _position._play_pos = _loop[_loopCount].pos;
+ _position._playPos = _loop[_loopCount].pos;
} else {
- _position._play_pos = _loop[_loopCount].pos;
+ _position._playPos = _loop[_loopCount].pos;
}
}
}
@@ -169,12 +164,12 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) {
case 0xF: // Meta or SysEx event
switch (info.event & 0x0F) {
case 0x2: // Song Position Pointer
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
break;
case 0x3: // Song Select
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
@@ -188,16 +183,16 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) {
break;
case 0x0: // SysEx
- info.length = readVLQ(_position._play_pos);
- info.ext.data = _position._play_pos;
- _position._play_pos += info.length;
+ info.length = readVLQ(_position._playPos);
+ info.ext.data = _position._playPos;
+ _position._playPos += info.length;
break;
case 0xF: // META event
- info.ext.type = *(_position._play_pos++);
- info.length = readVLQ(_position._play_pos);
- info.ext.data = _position._play_pos;
- _position._play_pos += info.length;
+ info.ext.type = *(_position._playPos++);
+ info.length = readVLQ(_position._playPos);
+ info.ext.data = _position._playPos;
+ _position._playPos += info.length;
if (info.ext.type == 0x51 && info.length == 3) {
// Tempo event. We want to make these constant 500,000.
info.ext.data[0] = 0x07;
@@ -216,7 +211,7 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
uint32 i = 0;
byte *start;
uint32 len;
- uint32 chunk_len;
+ uint32 chunkLen;
char buf[32];
_loopCount = -1;
@@ -235,7 +230,7 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
if (!memcmp(pos, "XMID", 4)) {
warning("XMIDI doesn't have XDIR");
pos += 4;
- _num_tracks = 1;
+ _numTracks = 1;
} else if (memcmp(pos, "XDIR", 4)) {
// Not an XMIDI that we recognize
warning("Expected 'XDIR' but found '%c%c%c%c'", pos[0], pos[1], pos[2], pos[3]);
@@ -243,7 +238,7 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
} else {
// Seems Valid
pos += 4;
- _num_tracks = 0;
+ _numTracks = 0;
for (i = 4; i < len; i++) {
// Read 4 bytes of type
@@ -251,34 +246,34 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
pos += 4;
// Read length of chunk
- chunk_len = read4high(pos);
+ chunkLen = read4high(pos);
// Add eight bytes
i += 8;
if (memcmp(buf, "INFO", 4) == 0) {
// Must be at least 2 bytes long
- if (chunk_len < 2) {
- warning("Invalid chunk length %d for 'INFO' block", (int)chunk_len);
+ if (chunkLen < 2) {
+ warning("Invalid chunk length %d for 'INFO' block", (int)chunkLen);
return false;
}
- _num_tracks = (byte)read2low(pos);
+ _numTracks = (byte)read2low(pos);
- if (chunk_len > 2) {
- warning("Chunk length %d is greater than 2", (int)chunk_len);
- //pos += chunk_len - 2;
+ if (chunkLen > 2) {
+ warning("Chunk length %d is greater than 2", (int)chunkLen);
+ //pos += chunkLen - 2;
}
break;
}
// Must align
- pos += (chunk_len + 1) & ~1;
- i += (chunk_len + 1) & ~1;
+ pos += (chunkLen + 1) & ~1;
+ i += (chunkLen + 1) & ~1;
}
// Didn't get to fill the header
- if (_num_tracks == 0) {
+ if (_numTracks == 0) {
warning("Didn't find a valid track count");
return false;
}
@@ -308,13 +303,13 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
// Ok it's an XMIDI.
// We're going to identify and store the location for each track.
- if (_num_tracks > ARRAYSIZE(_tracks)) {
- warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_num_tracks);
+ if (_numTracks > ARRAYSIZE(_tracks)) {
+ warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_numTracks);
return false;
}
- int tracks_read = 0;
- while (tracks_read < _num_tracks) {
+ int tracksRead = 0;
+ while (tracksRead < _numTracks) {
if (!memcmp(pos, "FORM", 4)) {
// Skip this plus the 4 bytes after it.
pos += 8;
@@ -330,11 +325,11 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
pos += (len + 1) & ~1;
} else if (!memcmp(pos, "EVNT", 4)) {
// Ahh! What we're looking for at last.
- _tracks[tracks_read] = pos + 8; // Skip the EVNT and length bytes
+ _tracks[tracksRead] = pos + 8; // Skip the EVNT and length bytes
pos += 4;
len = read4high(pos);
pos += (len + 1) & ~1;
- ++tracks_read;
+ ++tracksRead;
} else {
warning("Hit invalid block '%c%c%c%c' while scanning for track locations", pos[0], pos[1], pos[2], pos[3]);
return false;
@@ -349,7 +344,6 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
_ppqn = 60;
resetTracking();
setTempo(500000);
- _inserted_delta = 0;
setTrack(0);
return true;
}
@@ -357,11 +351,6 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
return false;
}
-void MidiParser_XMIDI::resetTracking() {
- MidiParser::resetTracking();
- _inserted_delta = 0;
-}
-
void MidiParser::defaultXMidiCallback(byte eventData, void *data) {
warning("MidiParser: defaultXMidiCallback(%d)", eventData);
}
diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp
index c9bd86b54a..75912f38a8 100644
--- a/audio/softsynth/mt32/Part.cpp
+++ b/audio/softsynth/mt32/Part.cpp
@@ -411,7 +411,7 @@ void RhythmPart::noteOn(unsigned int midiKey, unsigned int velocity) {
// According to info from Mok, keyShift does not appear to affect anything on rhythm part on LAPC-I, but may do on MT-32 - needs investigation
synth->printDebug(" Patch: (timbreGroup %u), (timbreNum %u), (keyShift %u), fineTune %u, benderRange %u, assignMode %u, (reverbSwitch %u)", patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, patchTemp->patch.keyShift, patchTemp->patch.fineTune, patchTemp->patch.benderRange, patchTemp->patch.assignMode, patchTemp->patch.reverbSwitch);
synth->printDebug(" PatchTemp: outputLevel %u, (panpot %u)", patchTemp->outputLevel, patchTemp->panpot);
- synth->printDebug(" RhythmTemp: timbre %u, outputLevel %u, panpot %u, reverbSwitch %u", rhythmTemp[drumNum].timbre, rhythmTemp[drumNum].outputLevel, rhythmTemp[drumNum].panpot, rhythmTemp[drumNum].reverbSwitch);
+ synth->printDebug(" RhythmTemp: timbre %u, outputLevel %u, panpot %u, reverbSwitch %u", rhythmTemp[drumNum].timbre, rhythmTemp[drumNum].outputLevel, rhythmTemp[drumNum].panpot, rhythmTemp[drumNum].reverbSwitch);
#endif
#endif
playPoly(drumCache[drumNum], &rhythmTemp[drumNum], midiKey, key, velocity);
diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h
index 95218c858c..5e250769ec 100644
--- a/audio/softsynth/mt32/Partial.h
+++ b/audio/softsynth/mt32/Partial.h
@@ -37,7 +37,7 @@ private:
const int debugPartialNum; // Only used for debugging
// Number of the sample currently being rendered by generateSamples(), or 0 if no run is in progress
// This is only kept available for debugging purposes.
- unsigned long sampleNum;
+ unsigned long sampleNum;
int ownerPart; // -1 if unassigned
int mixType;
diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp
index 42a3eaa179..0a6be826d6 100644
--- a/audio/softsynth/mt32/PartialManager.cpp
+++ b/audio/softsynth/mt32/PartialManager.cpp
@@ -148,7 +148,7 @@ bool PartialManager::abortFirstPolyPreferHeldWhereReserveExceeded(int minPart) {
bool PartialManager::freePartials(unsigned int needed, int partNum) {
// CONFIRMED: Barring bugs, this matches the real LAPC-I according to information from Mok.
- // BUG: There's a bug in the LAPC-I implementation:
+ // BUG: There's a bug in the LAPC-I implementation:
// When allocating for rhythm part, or when allocating for a part that is using fewer partials than it has reserved,
// held and playing polys on the rhythm part can potentially be aborted before releasing polys on the rhythm part.
// This bug isn't present on MT-32.
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 0861053b5c..7a1b5c2275 100644
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -1156,7 +1156,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le
DT(partial[x].tva.envLevel[0]); \
DT(partial[x].tva.envLevel[1]); \
DT(partial[x].tva.envLevel[2]); \
- DT(partial[x].tva.envLevel[3]);
+ DT(partial[x].tva.envLevel[3]);
DTP(0);
DTP(1);
diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp
index c3be6db591..f3e3f7bbc7 100644
--- a/audio/softsynth/mt32/TVA.cpp
+++ b/audio/softsynth/mt32/TVA.cpp
@@ -274,7 +274,7 @@ void TVA::nextPhase() {
}
int newTarget;
- int newIncrement;
+ int newIncrement = 0;
int envPointIndex = phase;
if (!allLevelsZeroFromNowOn) {
diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp
index 58f72e5a9b..80b592ea67 100644
--- a/audio/softsynth/mt32/TVF.cpp
+++ b/audio/softsynth/mt32/TVF.cpp
@@ -64,11 +64,11 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u
int biasPoint = partialParam->tvf.biasPoint;
if ((biasPoint & 0x40) == 0) {
// biasPoint range here: 0 to 63
- int bias = biasPoint + 33 - key; // bias range here: -75 to 84
+ int bias = biasPoint + 33 - key; // bias range here: -75 to 84
if (bias > 0) {
bias = -bias; // bias range here: -1 to -84
baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -7140 to 7140
- // baseCutoff range now: -10164 to 10164
+ // baseCutoff range now: -10164 to 10164
}
} else {
// biasPoint range here: 64 to 127
diff --git a/audio/softsynth/mt32/freeverb.cpp b/audio/softsynth/mt32/freeverb.cpp
index de8f2632cb..181b878596 100644
--- a/audio/softsynth/mt32/freeverb.cpp
+++ b/audio/softsynth/mt32/freeverb.cpp
@@ -202,7 +202,7 @@ void revmodel::process(const float *inputL, const float *inputR, float *outputL,
// Calculate output REPLACING anything already there
*outputL = outL*wet1 + outR*wet2;
*outputR = outR*wet1 + outL*wet2;
-
+
inputL++;
inputR++;
outputL++;
diff --git a/backends/events/openpandora/op-events.cpp b/backends/events/openpandora/op-events.cpp
index c1d6362fcb..fc63cdf74f 100644
--- a/backends/events/openpandora/op-events.cpp
+++ b/backends/events/openpandora/op-events.cpp
@@ -44,7 +44,8 @@ enum {
/* Touchscreen TapMode */
TAPMODE_LEFT = 0,
TAPMODE_RIGHT = 1,
- TAPMODE_HOVER = 2
+ TAPMODE_HOVER = 2,
+ TAPMODE_HOVER_DPAD = 3
};
OPEventSource::OPEventSource()
@@ -63,6 +64,8 @@ bool OPEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) {
event.type = Common::EVENT_RBUTTONDOWN;
else if (OP::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */
event.type = Common::EVENT_MOUSEMOVE;
+ else if (OP::tapmodeLevel == TAPMODE_HOVER_DPAD) /* TAPMODE_HOVER_DPAD = Hover (DPad Clicks) Tap Mode */
+ event.type = Common::EVENT_MOUSEMOVE;
else
event.type = Common::EVENT_LBUTTONDOWN; /* For normal mice etc. */
} else if (ev.button.button == SDL_BUTTON_RIGHT)
@@ -95,6 +98,8 @@ bool OPEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
event.type = Common::EVENT_RBUTTONUP;
else if (OP::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */
event.type = Common::EVENT_MOUSEMOVE;
+ else if (OP::tapmodeLevel == TAPMODE_HOVER_DPAD) /* TAPMODE_HOVER_DPAD = Hover (DPad Clicks) Tap Mode */
+ event.type = Common::EVENT_MOUSEMOVE;
else
event.type = Common::EVENT_LBUTTONUP; /* For normal mice etc. */
} else if (ev.button.button == SDL_BUTTON_RIGHT)
@@ -117,6 +122,30 @@ bool OPEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
bool OPEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
+ if (OP::tapmodeLevel == TAPMODE_HOVER_DPAD) {
+ switch (ev.key.keysym.sym) {
+ case SDLK_LEFT:
+ event.type = (ev.type == SDL_KEYDOWN) ? Common::EVENT_LBUTTONDOWN : Common::EVENT_LBUTTONUP;
+ processMouseEvent(event, _km.x, _km.y);
+ return true;
+ break;
+ case SDLK_RIGHT:
+ event.type = (ev.type == SDL_KEYDOWN) ? Common::EVENT_RBUTTONDOWN : Common::EVENT_RBUTTONUP;
+ processMouseEvent(event, _km.x, _km.y);
+ return true;
+ break;
+#if defined(SDL_BUTTON_MIDDLE)
+ case SDLK_UP:
+ event.type = (ev.type == SDL_KEYDOWN) ? Common::EVENT_MBUTTONDOWN : Common::EVENT_MBUTTONUP;
+ processMouseEvent(event, _km.x, _km.y);
+ return true;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
if (ev.type == SDL_KEYDOWN) {
switch (ev.key.keysym.sym) {
case SDLK_HOME:
@@ -141,6 +170,8 @@ bool OPEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
g_system->displayMessageOnOSD(_("Touchscreen 'Tap Mode' - Right Click"));
} else if (OP::tapmodeLevel == TAPMODE_HOVER) {
g_system->displayMessageOnOSD(_("Touchscreen 'Tap Mode' - Hover (No Click)"));
+ } else if (OP::tapmodeLevel == TAPMODE_HOVER_DPAD) {
+ g_system->displayMessageOnOSD(_("Touchscreen 'Tap Mode' - Hover (DPad Clicks)"));
}
break;
case SDLK_RSHIFT:
diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp
index f94171646a..0ca5bbb059 100644
--- a/backends/events/sdl/sdl-events.cpp
+++ b/backends/events/sdl/sdl-events.cpp
@@ -191,6 +191,8 @@ void SdlEventSource::SDLModToOSystemKeyFlags(SDLMod mod, Common::Event &event) {
#endif
if (mod & KMOD_CTRL)
event.kbd.flags |= Common::KBD_CTRL;
+ if (mod & KMOD_META)
+ event.kbd.flags |= Common::KBD_META;
// Sticky flags
if (mod & KMOD_NUM)
diff --git a/backends/events/sdl/sdl-events.h b/backends/events/sdl/sdl-events.h
index 2ba88c702b..ca4835126f 100644
--- a/backends/events/sdl/sdl-events.h
+++ b/backends/events/sdl/sdl-events.h
@@ -116,7 +116,7 @@ protected:
//@}
/**
- * Assigns the mouse coords to the mouse event. Furthermore notify the
+ * Assigns the mouse coords to the mouse event. Furthermore notify the
* graphics manager about the position change.
*/
virtual void processMouseEvent(Common::Event &event, int x, int y);
diff --git a/backends/events/webossdl/webossdl-events.h b/backends/events/webossdl/webossdl-events.h
index 99ed3105f8..1ba5c6fcbf 100644
--- a/backends/events/webossdl/webossdl-events.h
+++ b/backends/events/webossdl/webossdl-events.h
@@ -73,10 +73,10 @@ protected:
// The current mouse position on the screen.
int _curX, _curY;
-
+
// The current screen dimensions
int _screenX, _screenY;
-
+
// The drag distance for linear gestures
int _swipeDistX, _swipeDistY;
@@ -107,7 +107,7 @@ protected:
virtual bool handleMouseButtonUp(SDL_Event &ev, Common::Event &event);
virtual bool handleMouseMotion(SDL_Event &ev, Common::Event &event);
virtual bool pollEvent(Common::Event &event);
-
+
// Utility functions
void calculateDimensions();
};
diff --git a/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp b/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp
index f515343d3c..bd87c9fafd 100644
--- a/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp
+++ b/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp
@@ -479,7 +479,7 @@ void DINGUXSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable)
case OSystem::kFeatureCursorPalette:
_cursorPaletteDisabled = !enable;
blitCursor();
- break;
+ break;
default:
break;
}
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp
index 67041ae17b..fed02ef22e 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.cpp
+++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp
@@ -665,7 +665,7 @@ void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height)
void OpenGLSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) {
adjustMousePosition(point.x, point.y);
}
-
+
void OpenGLSdlGraphicsManager::notifyMousePos(Common::Point mouse) {
setMousePosition(mouse.x, mouse.y);
}
diff --git a/backends/midi/coreaudio.cpp b/backends/midi/coreaudio.cpp
index 43c801287d..94262d0d92 100644
--- a/backends/midi/coreaudio.cpp
+++ b/backends/midi/coreaudio.cpp
@@ -172,10 +172,15 @@ int MidiDriver_CORE::open() {
// Load custom soundfont, if specified
if (ConfMan.hasKey("soundfont")) {
- FSRef fsref;
- FSSpec fsSpec;
const char *soundfont = ConfMan.get("soundfont").c_str();
+ // TODO: We should really check whether the file contains an
+ // actual soundfont...
+
+#if USE_DEPRECATED_COREAUDIO_API
+ // Before 10.5, we need to use kMusicDeviceProperty_SoundBankFSSpec
+ FSRef fsref;
+ FSSpec fsSpec;
err = FSPathMakeRef ((const byte *)soundfont, &fsref, NULL);
if (err == noErr) {
@@ -183,8 +188,6 @@ int MidiDriver_CORE::open() {
}
if (err == noErr) {
- // TODO: We should really check here whether the file contains an
- // actual soundfont...
err = AudioUnitSetProperty (
_synth,
kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global,
@@ -192,9 +195,27 @@ int MidiDriver_CORE::open() {
&fsSpec, sizeof(fsSpec)
);
}
+#else
+ // kMusicDeviceProperty_SoundBankFSSpec is present on 10.6+, but broken
+ // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement
+ CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)soundfont, strlen(soundfont), false);
+
+ if (url) {
+ err = AudioUnitSetProperty (
+ _synth,
+ kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global,
+ 0,
+ &url, sizeof(url)
+ );
+
+ CFRelease(url);
+ } else {
+ warning("Failed to allocate CFURLRef from '%s'", soundfont);
+ }
+#endif
if (err != noErr)
- warning("Failed loading custom sound font '%s' (error %ld)\n", soundfont, (long)err);
+ error("Failed loading custom sound font '%s' (error %ld)", soundfont, (long)err);
}
#ifdef COREAUDIO_DISABLE_REVERB
diff --git a/backends/midi/sndio.cpp b/backends/midi/sndio.cpp
index 21c9ea4fec..a065a658e1 100644
--- a/backends/midi/sndio.cpp
+++ b/backends/midi/sndio.cpp
@@ -81,7 +81,7 @@ void MidiDriver_Sndio::send(uint32 b) {
if (!hdl)
return;
- buf[0] = b & 0xff;
+ buf[0] = b & 0xff;
buf[1] = (b >> 8) & 0xff;
buf[2] = (b >> 16) & 0xff;
buf[3] = (b >> 24) & 0xff;
@@ -101,7 +101,7 @@ void MidiDriver_Sndio::send(uint32 b) {
void MidiDriver_Sndio::sysEx(const byte *msg, uint16 length) {
if (!hdl)
return;
-
+
unsigned char buf[266];
assert(length + 2 <= ARRAYSIZE(buf));
diff --git a/backends/mixer/sdl13/sdl13-mixer.cpp b/backends/mixer/sdl13/sdl13-mixer.cpp
index 84777c8bab..24d3434fde 100644
--- a/backends/mixer/sdl13/sdl13-mixer.cpp
+++ b/backends/mixer/sdl13/sdl13-mixer.cpp
@@ -69,13 +69,13 @@ void Sdl13MixerManager::init() {
warning("Could not open audio device: %s", SDL_GetError());
_mixer = new Audio::MixerImpl(g_system, desired.freq);
- assert(_mixer);
+ assert(_mixer);
_mixer->setReady(false);
} else {
debug(1, "Output sample rate: %d Hz", _obtained.freq);
_mixer = new Audio::MixerImpl(g_system, _obtained.freq);
- assert(_mixer);
+ assert(_mixer);
_mixer->setReady(true);
startAudio();
diff --git a/backends/platform/bada/application.cpp b/backends/platform/bada/application.cpp
index ba8e544983..e761649245 100644
--- a/backends/platform/bada/application.cpp
+++ b/backends/platform/bada/application.cpp
@@ -103,7 +103,7 @@ void BadaScummVM::pauseGame(bool pause) {
if (pause && g_engine && !g_engine->isPaused()) {
_appForm->pushKey(Common::KEYCODE_SPACE);
}
-
+
if (g_system) {
((BadaSystem *)g_system)->setMute(pause);
}
diff --git a/backends/platform/bada/sscanf.cpp b/backends/platform/bada/sscanf.cpp
index b5e5b88cb5..aa846698f6 100644
--- a/backends/platform/bada/sscanf.cpp
+++ b/backends/platform/bada/sscanf.cpp
@@ -56,7 +56,7 @@ bool scanInt(const char **in, va_list *ap, int max) {
bool err = false;
if (end == *in || (max > 0 && (end - *in) > max)) {
- err = true;
+ err = true;
} else {
*arg = (int)n;
*in = end;
diff --git a/backends/platform/iphone/iphone_video.mm b/backends/platform/iphone/iphone_video.mm
index 7877bc6430..54e5d633d2 100644
--- a/backends/platform/iphone/iphone_video.mm
+++ b/backends/platform/iphone/iphone_video.mm
@@ -476,7 +476,7 @@ const char *iPhone_getDocumentsDir() {
else if (_videoContext.screenWidth == 640 && _videoContext.screenHeight == 400)
adjustedHeight = 480;
}
-
+
float overlayPortraitRatio;
if (_orientation == UIDeviceOrientationLandscapeLeft || _orientation == UIDeviceOrientationLandscapeRight) {
diff --git a/backends/platform/iphone/osys_video.mm b/backends/platform/iphone/osys_video.mm
index aa1856490f..ebe435cb25 100644
--- a/backends/platform/iphone/osys_video.mm
+++ b/backends/platform/iphone/osys_video.mm
@@ -353,7 +353,7 @@ void OSystem_IPHONE::copyRectToOverlay(const void *buf, int pitch, int x, int y,
}
byte *dst = (byte *)_videoContext->overlayTexture.getBasePtr(x, y);
- do {
+ do {
memcpy(dst, src, w * sizeof(uint16));
src += pitch;
dst += _videoContext->overlayTexture.pitch;
@@ -435,7 +435,7 @@ void OSystem_IPHONE::setCursorPalette(const byte *colors, uint start, uint num)
for (uint i = start; i < start + num; ++i, colors += 3)
_mouseCursorPalette[i] = Graphics::RGBToColor<Graphics::ColorMasks<5551> >(colors[0], colors[1], colors[2]);
-
+
// FIXME: This is just stupid, our client code seems to assume that this
// automatically enables the cursor palette.
_mouseCursorPaletteEnabled = true;
diff --git a/backends/platform/maemo/debian/rules b/backends/platform/maemo/debian/rules
index 43a34399a3..c713403876 100755
--- a/backends/platform/maemo/debian/rules
+++ b/backends/platform/maemo/debian/rules
@@ -47,7 +47,7 @@ install: build
install -m0644 gui/themes/scummclassic.zip gui/themes/scummmodern.zip debian/scummvm/opt/scummvm/share
install -m0644 backends/vkeybd/packs/vkeybd_default.zip debian/scummvm/opt/scummvm/share
# for optified version we can also add engine datafiles
- install -m0644 dists/engine-data/drascula.dat dists/engine-data/hugo.dat dists/engine-data/kyra.dat dists/engine-data/lure.dat dists/engine-data/queen.tbl dists/engine-data/sky.cpt dists/engine-data/teenagent.dat dists/engine-data/toon.dat debian/scummvm/opt/scummvm/share
+ install -m0644 dists/engine-data/drascula.dat dists/engine-data/hugo.dat dists/engine-data/kyra.dat dists/engine-data/lure.dat dists/engine-data/queen.tbl dists/engine-data/sky.cpt dists/engine-data/teenagent.dat dists/engine-data/tony.dat dists/engine-data/toon.dat debian/scummvm/opt/scummvm/share
install -m0644 -d debian/scummvm/usr/share/doc/scummvm
install -m0644 AUTHORS COPYING COPYING.BSD COPYING.FREEFONT COPYING.LGPL COPYRIGHT NEWS README debian/scummvm/usr/share/doc/scummvm
diff --git a/backends/platform/openpandora/op-options.cpp b/backends/platform/openpandora/op-options.cpp
index 58f0fb7188..005a76b76c 100644
--- a/backends/platform/openpandora/op-options.cpp
+++ b/backends/platform/openpandora/op-options.cpp
@@ -33,7 +33,8 @@ enum {
/* Touchscreen TapMode */
TAPMODE_LEFT = 0,
TAPMODE_RIGHT = 1,
- TAPMODE_HOVER = 2
+ TAPMODE_HOVER = 2,
+ TAPMODE_HOVER_DPAD = 3
};
int tapmodeLevel = TAPMODE_LEFT;
@@ -44,6 +45,8 @@ void ToggleTapMode() {
} else if (tapmodeLevel == TAPMODE_RIGHT) {
tapmodeLevel = TAPMODE_HOVER;
} else if (tapmodeLevel == TAPMODE_HOVER) {
+ tapmodeLevel = TAPMODE_HOVER_DPAD;
+ } else if (tapmodeLevel == TAPMODE_HOVER_DPAD) {
tapmodeLevel = TAPMODE_LEFT;
} else {
tapmodeLevel = TAPMODE_LEFT;
diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp
index 639bd980f6..fb76c111f2 100644
--- a/backends/platform/sdl/macosx/macosx.cpp
+++ b/backends/platform/sdl/macosx/macosx.cpp
@@ -156,7 +156,7 @@ Common::String OSystem_MacOSX::getSystemLanguage() const {
}
CFRelease(preferredLocalizations);
}
-
+
}
// Falback to POSIX implementation
return OSystem_POSIX::getSystemLanguage();
diff --git a/backends/platform/sdl/macosx/macosx.h b/backends/platform/sdl/macosx/macosx.h
index 4837e643b3..d9cb28b973 100644
--- a/backends/platform/sdl/macosx/macosx.h
+++ b/backends/platform/sdl/macosx/macosx.h
@@ -32,7 +32,7 @@ public:
virtual bool hasFeature(Feature f);
virtual bool displayLogFile();
-
+
virtual Common::String getSystemLanguage() const;
virtual void initBackend();
diff --git a/backends/platform/sdl/main.cpp b/backends/platform/sdl/main.cpp
deleted file mode 100644
index 040028079d..0000000000
--- a/backends/platform/sdl/main.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "common/scummsys.h"
-
-// Several SDL based ports use a custom main, and hence do not want to compile
-// of this file. The following "#if" ensures that.
-#if !defined(POSIX) && \
- !defined(WIN32) && \
- !defined(MAEMO) && \
- !defined(__SYMBIAN32__) && \
- !defined(_WIN32_WCE) && \
- !defined(__amigaos4__) && \
- !defined(DINGUX) && \
- !defined(CAANOO) && \
- !defined(LINUXMOTO) && \
- !defined(SAMSUNGTV) && \
- !defined(PLAYSTATION3) && \
- !defined(OPENPANDORA)
-
-#include "backends/platform/sdl/sdl.h"
-#include "backends/plugins/sdl/sdl-provider.h"
-#include "base/main.h"
-
-int main(int argc, char *argv[]) {
-
- // Create our OSystem instance
- g_system = new OSystem_SDL();
- assert(g_system);
-
- // Pre initialize the backend
- ((OSystem_SDL *)g_system)->init();
-
-#ifdef DYNAMIC_MODULES
- PluginManager::instance().addPluginProvider(new SDLPluginProvider());
-#endif
-
- // Invoke the actual ScummVM main entry point:
- int res = scummvm_main(argc, argv);
-
- // Free OSystem
- delete (OSystem_SDL *)g_system;
-
- return res;
-}
-
-#endif
diff --git a/backends/platform/sdl/module.mk b/backends/platform/sdl/module.mk
index 98a8265301..a17a326889 100644
--- a/backends/platform/sdl/module.mk
+++ b/backends/platform/sdl/module.mk
@@ -1,7 +1,6 @@
MODULE := backends/platform/sdl
MODULE_OBJS := \
- main.o \
sdl.o
ifdef POSIX
diff --git a/backends/taskbar/win32/mingw-compat.h b/backends/taskbar/win32/mingw-compat.h
index 55105407c6..f6151936f1 100644
--- a/backends/taskbar/win32/mingw-compat.h
+++ b/backends/taskbar/win32/mingw-compat.h
@@ -70,7 +70,7 @@ DECLARE_INTERFACE_(IPropertyStore, IUnknown) {
STDMETHOD (GetValue) (REFPROPERTYKEY key, PROPVARIANT *pv) PURE;
STDMETHOD (SetValue) (REFPROPERTYKEY key, REFPROPVARIANT propvar) PURE;
STDMETHOD (Commit) (void) PURE;
-
+
private:
~IPropertyStore();
};
@@ -137,7 +137,7 @@ DECLARE_INTERFACE_(ITaskbarList3, IUnknown) {
STDMETHOD (SetOverlayIcon) (THIS_ HWND hwnd, HICON hIcon, LPCWSTR pszDescription) PURE;
STDMETHOD (SetThumbnailTooltip) (THIS_ HWND hwnd, LPCWSTR pszTip) PURE;
STDMETHOD (SetThumbnailClip) (THIS_ HWND hwnd, RECT *prcClip) PURE;
-
+
private:
~ITaskbarList3();
};
diff --git a/backends/timer/bada/timer.cpp b/backends/timer/bada/timer.cpp
index f030e0695f..e41ecd4864 100644
--- a/backends/timer/bada/timer.cpp
+++ b/backends/timer/bada/timer.cpp
@@ -1,115 +1,115 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#if defined(BADA)
-
-#include "backends/timer/bada/timer.h"
-
-//
-// TimerSlot
-//
-TimerSlot::TimerSlot(Common::TimerManager::TimerProc callback,
- uint32 interval, void *refCon) :
- _timer(0),
- _callback(callback),
- _interval(interval),
- _refCon(refCon) {
-}
-
-TimerSlot::~TimerSlot() {
-}
-
-bool TimerSlot::OnStart() {
- _timer = new Osp::Base::Runtime::Timer();
- if (!_timer || IsFailed(_timer->Construct(*this))) {
- AppLog("Failed to create timer");
- return false;
- }
-
- if (IsFailed(_timer->Start(_interval))) {
- AppLog("failed to start timer");
- return false;
- }
-
- AppLog("started timer %d", _interval);
- return true;
-}
-
-void TimerSlot::OnStop() {
- AppLog("timer stopped");
- if (_timer) {
- _timer->Cancel();
- delete _timer;
- _timer = NULL;
- }
-}
-
-void TimerSlot::OnTimerExpired(Timer &timer) {
- _callback(_refCon);
- timer.Start(_interval);
-}
-
-//
-// BadaTimerManager
-//
-BadaTimerManager::BadaTimerManager() {
-}
-
-BadaTimerManager::~BadaTimerManager() {
- for (Common::List<TimerSlot>::iterator slot = _timers.begin();
- slot != _timers.end(); ) {
- slot->Stop();
- slot = _timers.erase(slot);
- }
-}
-
-bool BadaTimerManager::installTimerProc(TimerProc proc, int32 interval, void *refCon,
- const Common::String &id) {
- TimerSlot *slot = new TimerSlot(proc, interval / 1000, refCon);
-
- if (IsFailed(slot->Construct(THREAD_TYPE_EVENT_DRIVEN))) {
- AppLog("Failed to create timer thread");
- delete slot;
- return false;
- }
-
- if (IsFailed(slot->Start())) {
- delete slot;
- AppLog("Failed to start timer thread");
- return false;
- }
-
- _timers.push_back(*slot);
- return true;
-}
-
-void BadaTimerManager::removeTimerProc(TimerProc proc) {
- for (Common::List<TimerSlot>::iterator slot = _timers.begin();
- slot != _timers.end(); ++slot) {
- if (slot->_callback == proc) {
- slot->Stop();
- slot = _timers.erase(slot);
- }
- }
-}
-
-#endif
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if defined(BADA)
+
+#include "backends/timer/bada/timer.h"
+
+//
+// TimerSlot
+//
+TimerSlot::TimerSlot(Common::TimerManager::TimerProc callback,
+ uint32 interval, void *refCon) :
+ _timer(0),
+ _callback(callback),
+ _interval(interval),
+ _refCon(refCon) {
+}
+
+TimerSlot::~TimerSlot() {
+}
+
+bool TimerSlot::OnStart() {
+ _timer = new Osp::Base::Runtime::Timer();
+ if (!_timer || IsFailed(_timer->Construct(*this))) {
+ AppLog("Failed to create timer");
+ return false;
+ }
+
+ if (IsFailed(_timer->Start(_interval))) {
+ AppLog("failed to start timer");
+ return false;
+ }
+
+ AppLog("started timer %d", _interval);
+ return true;
+}
+
+void TimerSlot::OnStop() {
+ AppLog("timer stopped");
+ if (_timer) {
+ _timer->Cancel();
+ delete _timer;
+ _timer = NULL;
+ }
+}
+
+void TimerSlot::OnTimerExpired(Timer &timer) {
+ _callback(_refCon);
+ timer.Start(_interval);
+}
+
+//
+// BadaTimerManager
+//
+BadaTimerManager::BadaTimerManager() {
+}
+
+BadaTimerManager::~BadaTimerManager() {
+ for (Common::List<TimerSlot>::iterator slot = _timers.begin();
+ slot != _timers.end(); ) {
+ slot->Stop();
+ slot = _timers.erase(slot);
+ }
+}
+
+bool BadaTimerManager::installTimerProc(TimerProc proc, int32 interval, void *refCon,
+ const Common::String &id) {
+ TimerSlot *slot = new TimerSlot(proc, interval / 1000, refCon);
+
+ if (IsFailed(slot->Construct(THREAD_TYPE_EVENT_DRIVEN))) {
+ AppLog("Failed to create timer thread");
+ delete slot;
+ return false;
+ }
+
+ if (IsFailed(slot->Start())) {
+ delete slot;
+ AppLog("Failed to start timer thread");
+ return false;
+ }
+
+ _timers.push_back(*slot);
+ return true;
+}
+
+void BadaTimerManager::removeTimerProc(TimerProc proc) {
+ for (Common::List<TimerSlot>::iterator slot = _timers.begin();
+ slot != _timers.end(); ++slot) {
+ if (slot->_callback == proc) {
+ slot->Stop();
+ slot = _timers.erase(slot);
+ }
+ }
+}
+
+#endif
diff --git a/backends/timer/bada/timer.h b/backends/timer/bada/timer.h
index 04ca771c26..826064d7ff 100644
--- a/backends/timer/bada/timer.h
+++ b/backends/timer/bada/timer.h
@@ -1,62 +1,62 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef BADA_TIMER_H
-#define BADA_TIMER_H
-
-#include <FBase.h>
-
-#include "common/timer.h"
-#include "common/list.h"
-
-using namespace Osp::Base::Runtime;
-
-struct TimerSlot: public ITimerEventListener, public Thread {
- TimerSlot(Common::TimerManager::TimerProc callback,
- uint32 interval,
- void *refCon);
- ~TimerSlot();
-
- bool OnStart(void);
- void OnStop(void);
- void OnTimerExpired(Timer &timer);
-
- Timer *_timer;
- Common::TimerManager::TimerProc _callback;
- uint32 _interval; // in microseconds
- void *_refCon;
-};
-
-class BadaTimerManager : public Common::TimerManager {
-public:
- BadaTimerManager();
- ~BadaTimerManager();
-
- bool installTimerProc(TimerProc proc, int32 interval, void *refCon,
- const Common::String &id);
- void removeTimerProc(TimerProc proc);
-
-private:
- Common::List<TimerSlot> _timers;
-};
-
-#endif
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BADA_TIMER_H
+#define BADA_TIMER_H
+
+#include <FBase.h>
+
+#include "common/timer.h"
+#include "common/list.h"
+
+using namespace Osp::Base::Runtime;
+
+struct TimerSlot: public ITimerEventListener, public Thread {
+ TimerSlot(Common::TimerManager::TimerProc callback,
+ uint32 interval,
+ void *refCon);
+ ~TimerSlot();
+
+ bool OnStart(void);
+ void OnStop(void);
+ void OnTimerExpired(Timer &timer);
+
+ Timer *_timer;
+ Common::TimerManager::TimerProc _callback;
+ uint32 _interval; // in microseconds
+ void *_refCon;
+};
+
+class BadaTimerManager : public Common::TimerManager {
+public:
+ BadaTimerManager();
+ ~BadaTimerManager();
+
+ bool installTimerProc(TimerProc proc, int32 interval, void *refCon,
+ const Common::String &id);
+ void removeTimerProc(TimerProc proc);
+
+private:
+ Common::List<TimerSlot> _timers;
+};
+
+#endif
diff --git a/backends/timer/default/default-timer.cpp b/backends/timer/default/default-timer.cpp
index 9cd803f148..9f56d58b12 100644
--- a/backends/timer/default/default-timer.cpp
+++ b/backends/timer/default/default-timer.cpp
@@ -156,7 +156,7 @@ void DefaultTimerManager::removeTimerProc(TimerProc callback) {
}
// We need to remove all names referencing the timer proc here.
- //
+ //
// Else we run into troubles, when the client code removes and readds timer
// callbacks.
//
diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 5ad23313dc..44007c494a 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -238,7 +238,7 @@ void registerDefaults() {
ConfMan.registerDefault("record_time_file_name", "record.time");
ConfMan.registerDefault("gui_saveload_chooser", "grid");
-
+ ConfMan.registerDefault("gui_saveload_last_pos", "0");
}
//
diff --git a/base/main.cpp b/base/main.cpp
index 25e1b881cc..355a65f883 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -57,6 +57,7 @@
#include "graphics/cursorman.h"
#include "graphics/fontman.h"
+#include "graphics/yuv_to_rgb.h"
#ifdef USE_FREETYPE2
#include "graphics/fonts/ttf.h"
#endif
@@ -511,6 +512,8 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
#ifdef USE_FREETYPE2
Graphics::shutdownTTF();
#endif
+ EngineManager::destroy();
+ Graphics::YUVToRGBManager::destroy();
return 0;
}
diff --git a/common/array.h b/common/array.h
index a2c3023362..ca89523a0b 100644
--- a/common/array.h
+++ b/common/array.h
@@ -332,7 +332,7 @@ protected:
// Copy a part of the new data to the position inside the
// initialized space.
copy(first, first + (_size - idx), pos);
-
+
// Copy a part of the new data to the position inside the
// uninitialized space.
uninitialized_copy(first + (_size - idx), last, _storage + _size);
diff --git a/common/coroutines.cpp b/common/coroutines.cpp
index 042b15b5d7..849b881177 100644
--- a/common/coroutines.cpp
+++ b/common/coroutines.cpp
@@ -433,9 +433,9 @@ void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *
// Determine the signalled state
_ctx->pidSignalled = (_ctx->pProcess) || !_ctx->pEvent ? false : _ctx->pEvent->signalled;
- if (bWaitAll && _ctx->pidSignalled)
+ if (bWaitAll && !_ctx->pidSignalled)
_ctx->signalled = false;
- else if (!bWaitAll & _ctx->pidSignalled)
+ else if (!bWaitAll && _ctx->pidSignalled)
_ctx->signalled = true;
}
@@ -445,7 +445,7 @@ void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *
for (_ctx->i = 0; _ctx->i < nCount; ++_ctx->i) {
_ctx->pEvent = getEvent(pidList[_ctx->i]);
- if (_ctx->pEvent->manualReset)
+ if (!_ctx->pEvent->manualReset)
_ctx->pEvent->signalled = false;
}
@@ -717,12 +717,12 @@ void CoroutineScheduler::pulseEvent(uint32 pidEvent) {
EVENT *evt = getEvent(pidEvent);
if (!evt)
return;
-
+
// Set the event as signalled and pulsing
evt->signalled = true;
evt->pulsing = true;
- // If there's an active process, and it's not the first in the queue, then reschedule all
+ // If there's an active process, and it's not the first in the queue, then reschedule all
// the other prcoesses in the queue to run again this frame
if (pCurrent && pCurrent != active->pNext)
rescheduleAll();
diff --git a/common/cosinetables.cpp b/common/cosinetables.cpp
index bf158e904f..fe8f454e14 100644
--- a/common/cosinetables.cpp
+++ b/common/cosinetables.cpp
@@ -36,7 +36,7 @@ CosineTable::CosineTable(int bitPrecision) {
double freq = 2 * M_PI / m;
_table = new float[m];
- // Table contains cos(2*pi*x/n) for 0<=x<=n/4,
+ // Table contains cos(2*pi*x/n) for 0<=x<=n/4,
// followed by its reverse
for (int i = 0; i <= m / 4; i++)
_table[i] = cos(i * freq);
diff --git a/common/gui_options.h b/common/gui_options.h
index 9da19b1c3e..447fff43ed 100644
--- a/common/gui_options.h
+++ b/common/gui_options.h
@@ -55,7 +55,7 @@
#define GUIO_RENDERPC9821 "\037"
#define GUIO_RENDERPC9801 "\040"
-// Special GUIO flags for the AdvancedDetector's caching of game specific
+// Special GUIO flags for the AdvancedDetector's caching of game specific
// options.
#define GUIO_GAMEOPTIONS1 "\041"
#define GUIO_GAMEOPTIONS2 "\042"
diff --git a/engines/agos/installshield_cab.cpp b/common/installshield_cab.cpp
index d4e636f7b3..e25d14741a 100644
--- a/engines/agos/installshield_cab.cpp
+++ b/common/installshield_cab.cpp
@@ -43,27 +43,25 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
-#include "agos/installshield_cab.h"
-
+#include "common/archive.h"
#include "common/debug.h"
-#include "common/file.h"
+#include "common/hash-str.h"
+#include "common/installshield_cab.h"
#include "common/memstream.h"
#include "common/zlib.h"
-namespace AGOS {
-
-class InstallShieldCabinet : public Common::Archive {
- Common::String _installShieldFilename;
+namespace Common {
+class InstallShieldCabinet : public Archive {
public:
- InstallShieldCabinet(const Common::String &filename);
+ InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
~InstallShieldCabinet();
- // Common::Archive API implementation
- bool hasFile(const Common::String &name) const;
- int listMembers(Common::ArchiveMemberList &list) const;
- const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
- Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+ // Archive API implementation
+ bool hasFile(const String &name) const;
+ int listMembers(ArchiveMemberList &list) const;
+ const ArchiveMemberPtr getMember(const String &name) const;
+ SeekableReadStream *createReadStreamForMember(const String &name) const;
private:
struct FileEntry {
@@ -73,53 +71,51 @@ private:
uint16 flags;
};
- typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+ typedef HashMap<String, FileEntry, IgnoreCase_Hash, IgnoreCase_EqualTo> FileMap;
FileMap _map;
+ Common::SeekableReadStream *_stream;
+ DisposeAfterUse::Flag _disposeAfterUse;
};
InstallShieldCabinet::~InstallShieldCabinet() {
_map.clear();
-}
-
-InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _installShieldFilename(filename) {
- Common::File installShieldFile;
- if (!installShieldFile.open(_installShieldFilename)) {
- warning("InstallShieldCabinet::InstallShieldCabinet(): Could not find the archive file %s", _installShieldFilename.c_str());
- return;
- }
+ if (_disposeAfterUse == DisposeAfterUse::YES)
+ delete _stream;
+}
+InstallShieldCabinet::InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse) {
// Note that we only support a limited subset of cabinet files
// Only single cabinet files and ones without data shared between
// cabinets.
// Check for the magic uint32
- if (installShieldFile.readUint32LE() != 0x28635349) {
+ if (_stream->readUint32LE() != 0x28635349) {
warning("InstallShieldCabinet::InstallShieldCabinet(): Magic ID doesn't match");
return;
}
- uint32 version = installShieldFile.readUint32LE();
+ uint32 version = _stream->readUint32LE();
if (version != 0x01000004) {
warning("Unsupported CAB version %08x", version);
return;
}
- /* uint32 volumeInfo = */ installShieldFile.readUint32LE();
- uint32 cabDescriptorOffset = installShieldFile.readUint32LE();
- /* uint32 cabDescriptorSize = */ installShieldFile.readUint32LE();
+ /* uint32 volumeInfo = */ _stream->readUint32LE();
+ uint32 cabDescriptorOffset = _stream->readUint32LE();
+ /* uint32 cabDescriptorSize = */ _stream->readUint32LE();
- installShieldFile.seek(cabDescriptorOffset);
+ _stream->seek(cabDescriptorOffset);
- installShieldFile.skip(12);
- uint32 fileTableOffset = installShieldFile.readUint32LE();
- installShieldFile.skip(4);
- uint32 fileTableSize = installShieldFile.readUint32LE();
- uint32 fileTableSize2 = installShieldFile.readUint32LE();
- uint32 directoryCount = installShieldFile.readUint32LE();
- installShieldFile.skip(8);
- uint32 fileCount = installShieldFile.readUint32LE();
+ _stream->skip(12);
+ uint32 fileTableOffset = _stream->readUint32LE();
+ _stream->skip(4);
+ uint32 fileTableSize = _stream->readUint32LE();
+ uint32 fileTableSize2 = _stream->readUint32LE();
+ uint32 directoryCount = _stream->readUint32LE();
+ _stream->skip(8);
+ uint32 fileCount = _stream->readUint32LE();
if (fileTableSize != fileTableSize2)
warning("file table sizes do not match");
@@ -127,33 +123,33 @@ InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _in
// We're ignoring file groups and components since we
// should not need them. Moving on to the files...
- installShieldFile.seek(cabDescriptorOffset + fileTableOffset);
+ _stream->seek(cabDescriptorOffset + fileTableOffset);
uint32 fileTableCount = directoryCount + fileCount;
uint32 *fileTableOffsets = new uint32[fileTableCount];
for (uint32 i = 0; i < fileTableCount; i++)
- fileTableOffsets[i] = installShieldFile.readUint32LE();
+ fileTableOffsets[i] = _stream->readUint32LE();
for (uint32 i = directoryCount; i < fileCount + directoryCount; i++) {
- installShieldFile.seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]);
- uint32 nameOffset = installShieldFile.readUint32LE();
- /* uint32 directoryIndex = */ installShieldFile.readUint32LE();
+ _stream->seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]);
+ uint32 nameOffset = _stream->readUint32LE();
+ /* uint32 directoryIndex = */ _stream->readUint32LE();
// First read in data needed by us to get at the file data
FileEntry entry;
- entry.flags = installShieldFile.readUint16LE();
- entry.uncompressedSize = installShieldFile.readUint32LE();
- entry.compressedSize = installShieldFile.readUint32LE();
- installShieldFile.skip(20);
- entry.offset = installShieldFile.readUint32LE();
+ entry.flags = _stream->readUint16LE();
+ entry.uncompressedSize = _stream->readUint32LE();
+ entry.compressedSize = _stream->readUint32LE();
+ _stream->skip(20);
+ entry.offset = _stream->readUint32LE();
// Then let's get the string
- installShieldFile.seek(cabDescriptorOffset + fileTableOffset + nameOffset);
- Common::String fileName;
+ _stream->seek(cabDescriptorOffset + fileTableOffset + nameOffset);
+ String fileName;
- char c = installShieldFile.readByte();
+ char c = _stream->readByte();
while (c) {
fileName += c;
- c = installShieldFile.readByte();
+ c = _stream->readByte();
}
_map[fileName] = entry;
}
@@ -161,43 +157,39 @@ InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _in
delete[] fileTableOffsets;
}
-bool InstallShieldCabinet::hasFile(const Common::String &name) const {
+bool InstallShieldCabinet::hasFile(const String &name) const {
return _map.contains(name);
}
-int InstallShieldCabinet::listMembers(Common::ArchiveMemberList &list) const {
+int InstallShieldCabinet::listMembers(ArchiveMemberList &list) const {
for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++)
list.push_back(getMember(it->_key));
return _map.size();
}
-const Common::ArchiveMemberPtr InstallShieldCabinet::getMember(const Common::String &name) const {
- return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+const ArchiveMemberPtr InstallShieldCabinet::getMember(const String &name) const {
+ return ArchiveMemberPtr(new GenericArchiveMember(name, this));
}
-Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const Common::String &name) const {
+SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const String &name) const {
if (!_map.contains(name))
return 0;
const FileEntry &entry = _map[name];
- Common::File archiveFile;
- archiveFile.open(_installShieldFilename);
- archiveFile.seek(entry.offset);
+ _stream->seek(entry.offset);
- if (!(entry.flags & 0x04)) {
- // Not compressed
- return archiveFile.readStream(entry.uncompressedSize);
- }
+ if (!(entry.flags & 0x04)) // Not compressed
+ return _stream->readStream(entry.uncompressedSize);
#ifdef USE_ZLIB
byte *src = (byte *)malloc(entry.compressedSize);
byte *dst = (byte *)malloc(entry.uncompressedSize);
- archiveFile.read(src, entry.compressedSize);
+ _stream->read(src, entry.compressedSize);
- bool result = Common::inflateZlibHeaderless(dst, entry.uncompressedSize, src, entry.compressedSize);
+ bool result = inflateZlibInstallShield(dst, entry.uncompressedSize, src, entry.compressedSize);
free(src);
if (!result) {
@@ -206,15 +198,15 @@ Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(cons
return 0;
}
- return new Common::MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES);
+ return new MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES);
#else
warning("zlib required to extract compressed CAB file '%s'", name.c_str());
return 0;
#endif
}
-Common::Archive *makeInstallShieldArchive(const Common::String &name) {
- return new InstallShieldCabinet(name);
+Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+ return new InstallShieldCabinet(stream, disposeAfterUse);
}
} // End of namespace AGOS
diff --git a/engines/agos/installshield_cab.h b/common/installshield_cab.h
index f7e8bed277..7c4f294578 100644
--- a/engines/agos/installshield_cab.h
+++ b/common/installshield_cab.h
@@ -20,22 +20,24 @@
*
*/
-#include "common/archive.h"
-#include "common/str.h"
+#ifndef COMMON_INSTALLSHIELD_CAB_H
+#define COMMON_INSTALLSHIELD_CAB_H
-#ifndef AGOS_INSTALLSHIELD_CAB_H
-#define AGOS_INSTALLSHIELD_CAB_H
+#include "common/types.h"
-namespace AGOS {
+namespace Common {
+
+class Archive;
+class SeekableReadStream;
/**
* This factory method creates an Archive instance corresponding to the content
- * of the InstallShield compressed file with the given name.
+ * of the InstallShield compressed stream.
*
* May return 0 in case of a failure.
*/
-Common::Archive *makeInstallShieldArchive(const Common::String &name);
+Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
-} // End of namespace AGOS
+} // End of namespace Common
#endif
diff --git a/common/keyboard.h b/common/keyboard.h
index f9e94e6656..3262a15c3f 100644
--- a/common/keyboard.h
+++ b/common/keyboard.h
@@ -224,12 +224,13 @@ enum {
KBD_CTRL = 1 << 0,
KBD_ALT = 1 << 1,
KBD_SHIFT = 1 << 2,
- KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT),
+ KBD_META = 1 << 3,
+ KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT|KBD_META),
// Sticky modifier flags
- KBD_NUM = 1 << 3,
- KBD_CAPS = 1 << 4,
- KBD_SCRL = 1 << 5,
+ KBD_NUM = 1 << 4,
+ KBD_CAPS = 1 << 5,
+ KBD_SCRL = 1 << 6,
KBD_STICKY = (KBD_NUM|KBD_CAPS|KBD_SCRL)
};
diff --git a/common/macresman.cpp b/common/macresman.cpp
index 14bdfa7080..f2f020c6de 100644
--- a/common/macresman.cpp
+++ b/common/macresman.cpp
@@ -124,7 +124,7 @@ bool MacResManager::open(String filename) {
File *file = new File();
// First, let's try to see if the Mac converted name exists
- if (file->open("._" + filename) && loadFromAppleDouble(*file)) {
+ if (file->open(constructAppleDoubleName(filename)) && loadFromAppleDouble(*file)) {
_baseFileName = filename;
return true;
}
@@ -185,7 +185,7 @@ bool MacResManager::open(FSNode path, String filename) {
#endif
// First, let's try to see if the Mac converted name exists
- FSNode fsNode = path.getChild("._" + filename);
+ FSNode fsNode = path.getChild(constructAppleDoubleName(filename));
if (fsNode.exists() && !fsNode.isDirectory()) {
SeekableReadStream *stream = fsNode.createReadStream();
if (loadFromAppleDouble(*stream)) {
@@ -253,7 +253,7 @@ bool MacResManager::exists(const String &filename) {
return true;
// Check if we have an AppleDouble file
- if (tempFile.open("._" + filename) && tempFile.readUint32BE() == 0x00051607)
+ if (tempFile.open(constructAppleDoubleName(filename)) && tempFile.readUint32BE() == 0x00051607)
return true;
return false;
@@ -574,4 +574,20 @@ void MacResManager::readMap() {
}
}
+Common::String MacResManager::constructAppleDoubleName(Common::String name) {
+ // Insert "._" before the last portion of a path name
+ for (int i = name.size() - 1; i >= 0; i--) {
+ if (i == 0) {
+ name.insertChar('_', 0);
+ name.insertChar('.', 0);
+ } else if (name[i] == '/') {
+ name.insertChar('_', i + 1);
+ name.insertChar('.', i + 1);
+ break;
+ }
+ }
+
+ return name;
+}
+
} // End of namespace Common
diff --git a/common/macresman.h b/common/macresman.h
index 6820106925..ed74da9cc6 100644
--- a/common/macresman.h
+++ b/common/macresman.h
@@ -25,6 +25,7 @@
* Macintosh resource fork manager used in engines:
* - groovie
* - mohawk
+ * - pegasus
* - sci
* - scumm
*/
@@ -175,6 +176,8 @@ private:
bool loadFromMacBinary(SeekableReadStream &stream);
bool loadFromAppleDouble(SeekableReadStream &stream);
+ static Common::String constructAppleDoubleName(Common::String name);
+
enum {
kResForkNone = 0,
kResForkRaw,
diff --git a/common/module.mk b/common/module.mk
index 92279740e5..d96b11ee40 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -16,6 +16,7 @@ MODULE_OBJS := \
gui_options.o \
hashmap.o \
iff_container.o \
+ installshield_cab.o \
language.o \
localization.o \
macresman.o \
diff --git a/common/quicktime.h b/common/quicktime.h
index 08ca35ad51..641718e13a 100644
--- a/common/quicktime.h
+++ b/common/quicktime.h
@@ -168,7 +168,7 @@ protected:
Rational _scaleFactorX;
Rational _scaleFactorY;
Array<Track *> _tracks;
-
+
void init();
private:
diff --git a/common/rational.h b/common/rational.h
index 45aa6a7a20..8270d2194e 100644
--- a/common/rational.h
+++ b/common/rational.h
@@ -80,6 +80,9 @@ public:
double toDouble() const;
frac_t toFrac() const;
+ int getNumerator() const { return _num; }
+ int getDenominator() const { return _denom; }
+
void debugPrint(int debuglevel = 0, const char *caption = "Rational:") const;
private:
diff --git a/common/rect.h b/common/rect.h
index 2bd3affafe..8d1243f7e4 100644
--- a/common/rect.h
+++ b/common/rect.h
@@ -170,6 +170,20 @@ struct Rect {
}
/**
+ * Find the intersecting rectangle between this rectangle and the given rectangle
+ *
+ * @param r the intersecting rectangle
+ *
+ * @return the intersection of the rectangles or an empty rectangle if not intersecting
+ */
+ Rect findIntersectingRect(const Rect &r) const {
+ if (!intersects(r))
+ return Rect();
+
+ return Rect(MAX(r.left, left), MAX(r.top, top), MIN(r.right, right), MIN(r.bottom, bottom));
+ }
+
+ /**
* Extend this rectangle so that it contains r
*
* @param r the rectangle to extend by
diff --git a/common/savefile.h b/common/savefile.h
index da787289ee..19536da54f 100644
--- a/common/savefile.h
+++ b/common/savefile.h
@@ -109,12 +109,12 @@ public:
*
* Saved games are compressed by default, and engines are expected to
* always write compressed saves.
- *
+ *
* A notable exception is if uncompressed files are needed for
* compatibility with games not supported by ScummVM, such as character
* exports from the Quest for Glory series. QfG5 is a 3D game and won't be
* supported by ScummVM.
- *
+ *
* @param name the name of the savefile
* @param compress toggles whether to compress the resulting save file
* (default) or not.
diff --git a/common/sinetables.cpp b/common/sinetables.cpp
index a4467383cc..a6ec99469d 100644
--- a/common/sinetables.cpp
+++ b/common/sinetables.cpp
@@ -36,7 +36,7 @@ SineTable::SineTable(int bitPrecision) {
double freq = 2 * M_PI / m;
_table = new float[m];
- // Table contains sin(2*pi*x/n) for 0<=x<=n/4,
+ // Table contains sin(2*pi*x/n) for 0<=x<=n/4,
// followed by its reverse
for (int i = 0; i <= m / 4; i++)
_table[i] = sin(i * freq);
diff --git a/common/zlib.cpp b/common/zlib.cpp
index 76e34485da..fc8f351054 100644
--- a/common/zlib.cpp
+++ b/common/zlib.cpp
@@ -85,6 +85,60 @@ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen,
return true;
}
+enum {
+ kTempBufSize = 65536
+};
+
+bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen) {
+ if (!dst || !dstLen || !src || !srcLen)
+ return false;
+
+ // See if we have sync bytes. If so, just use our function for that.
+ if (srcLen >= 4 && READ_BE_UINT32(src + srcLen - 4) == 0xFFFF)
+ return inflateZlibHeaderless(dst, dstLen, src, srcLen);
+
+ // Otherwise, we have some custom code we get to use here.
+
+ byte *temp = (byte *)malloc(kTempBufSize);
+
+ uint32 bytesRead = 0, bytesProcessed = 0;
+ while (bytesRead < srcLen) {
+ uint16 chunkSize = READ_LE_UINT16(src + bytesRead);
+ bytesRead += 2;
+
+ // Initialize zlib
+ z_stream stream;
+ stream.next_in = const_cast<byte *>(src + bytesRead);
+ stream.avail_in = chunkSize;
+ stream.next_out = temp;
+ stream.avail_out = kTempBufSize;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ // Negative MAX_WBITS tells zlib there's no zlib header
+ int err = inflateInit2(&stream, -MAX_WBITS);
+ if (err != Z_OK)
+ return false;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_OK && err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ free(temp);
+ return false;
+ }
+
+ memcpy(dst + bytesProcessed, temp, stream.total_out);
+ bytesProcessed += stream.total_out;
+
+ inflateEnd(&stream);
+ bytesRead += chunkSize;
+ }
+
+ free(temp);
+ return true;
+}
+
/**
* A simple wrapper class which can be used to wrap around an arbitrary
* other SeekableReadStream and will then provide on-the-fly decompression support.
diff --git a/common/zlib.h b/common/zlib.h
index 8372499922..6a840f5fdc 100644
--- a/common/zlib.h
+++ b/common/zlib.h
@@ -77,6 +77,25 @@ bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long
*/
bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, const byte *dict = 0, uint dictLen = 0);
+/**
+ * Wrapper around zlib's inflate functions. This function will call the
+ * necessary inflate functions to uncompress data compressed for InstallShield
+ * cabinet files.
+ *
+ * Decompresses the src buffer into the dst buffer.
+ * srcLen is the byte length of the source buffer, dstLen is the byte
+ * length of the output buffer.
+ * It decompress as much data as possible, up to dstLen bytes.
+ *
+ * @param dst the buffer to store into.
+ * @param dstLen the size of the destination buffer.
+ * @param src the data to be decompressed.
+ * @param dstLen the size of the compressed data.
+ *
+ * @return true on success (Z_OK or Z_STREAM_END), false otherwise.
+ */
+bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen);
+
#endif
/**
@@ -95,7 +114,7 @@ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen,
* returned).
*
* @param toBeWrapped the stream to be wrapped (if it is in gzip-format)
- * @param knownSize a supplied length of the compressed data (if not available directly)
+ * @param knownSize a supplied length of the compressed data (if not available directly)
*/
SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize = 0);
diff --git a/configure b/configure
index 5c6deace41..a0119bb1b1 100755
--- a/configure
+++ b/configure
@@ -62,7 +62,7 @@ get_var() {
eval echo \$${1}
}
-# Add an engine: id name build subengines
+# Add an engine: id name build subengines base-games dependencies
add_engine() {
_engines="${_engines} ${1}"
if test "${3}" = "no" ; then
@@ -72,11 +72,22 @@ add_engine() {
set_var _engine_${1}_build "${3}"
set_var _engine_${1}_build_default "${3}"
set_var _engine_${1}_subengines "${4}"
+ set_var _engine_${1}_base "${5}"
+ set_var _engine_${1}_deps "${6}"
for sub in ${4}; do
set_var _engine_${sub}_sub "yes"
+ set_var _engine_${sub}_parent "${1}"
done
}
+# Add a feature: id name settings-list
+add_feature() {
+ set_var _feature_${1}_name "${2}"
+ # This is a list of settings, where one must be "yes" for the feature to
+ # be enabled
+ set_var _feature_${1}_settings "${3}"
+}
+
_srcdir=`dirname $0`
# Read list of engines
@@ -85,7 +96,7 @@ _srcdir=`dirname $0`
#
# Default settings
#
-# Default lib behaviour yes/no/auto
+# Default lib behavior yes/no/auto
_vorbis=auto
_tremor=auto
_tremolo=no
@@ -108,7 +119,7 @@ _freetype2=auto
_taskbar=yes
_updates=no
_libunity=auto
-# Default option behaviour yes/no
+# Default option behavior yes/no
_debug_build=auto
_release_build=auto
_optimizations=auto
@@ -162,6 +173,17 @@ _endian=unknown
_need_memalign=yes
_have_x86=no
+# Add (virtual) features
+add_feature 16bit "16bit color" "_16bit"
+add_feature faad "libfaad" "_faad"
+add_feature flac "FLAC" "_flac"
+add_feature freetype2 "FreeType2" "_freetype2"
+add_feature mad "MAD" "_mad"
+add_feature png "PNG" "_png"
+add_feature theoradec "libtheoradec" "_theoradec"
+add_feature vorbis "Vorbis file support" "_vorbis _tremor"
+add_feature zlib "zlib" "_zlib"
+
# Directories for installing ScummVM.
@@ -439,6 +461,26 @@ Try \`$0 --help' for more information." >&2
}
+#
+# Feature handling functions
+#
+
+# Get the name of the feature
+get_feature_name() {
+ get_var _feature_$1_name
+}
+
+# Check whether the feature is enabled
+get_feature_state() {
+ for i in `get_var _feature_$1_settings`; do
+ if test `get_var $i` = "yes"; then
+ echo "yes"
+ return
+ fi
+ done
+ echo "no"
+}
+
#
# Engine handling functions
@@ -464,6 +506,16 @@ get_engine_subengines() {
get_var _engine_$1_subengines
}
+# Get the dependencies
+get_engine_dependencies() {
+ get_var _engine_$1_deps
+}
+
+# Get the base engine game support description
+get_engine_base() {
+ get_var _engine_$1_base
+}
+
# Ask if this is a subengine
get_engine_sub() {
sub=`get_var _engine_$1_sub`
@@ -473,6 +525,11 @@ get_engine_sub() {
echo $sub
}
+# Get a subengine's parent (undefined for non-subengines)
+get_subengine_parent() {
+ get_var _engine_$1_parent
+}
+
# Enable *all* engines
engine_enable_all() {
for engine in $_engines; do
@@ -500,9 +557,15 @@ engine_enable() {
engine=`echo $eng | sed 's/-/_/g'`
# Filter the parameter for the subengines
- if test "`get_engine_sub ${engine}`" != "no" -a "$opt" != "yes" ; then
- subengine_option_error ${engine}
- return
+ if test "`get_engine_sub ${engine}`" != "no" ; then
+ if test "$opt" != "yes" ; then
+ subengine_option_error ${engine}
+ return
+ fi
+ parent=`get_subengine_parent ${engine}`
+ if test `get_engine_build ${parent}` = "no" ; then
+ set_var _engine_${parent}_build "yes"
+ fi
fi
if test "$opt" = "static" -o "$opt" = "dynamic" -o "$opt" = "yes" ; then
@@ -532,6 +595,29 @@ engine_disable() {
fi
}
+# Check whether the engine's dependencies are met
+# If that is not the case disable the engine
+check_engine_deps() {
+ unmet_deps=""
+
+ # Check whether the engine is enabled
+ if test `get_engine_build $1` != "no" ; then
+ # Collect unmet dependencies
+ for dep in `get_engine_dependencies $1`; do
+ if test `get_feature_state $dep` = "no"; then
+ feature_name=`get_feature_name $dep`
+ unmet_deps="${unmet_deps}${feature_name} "
+ fi
+ done
+
+ # Check whether there is any unmet dependency
+ if test -n "$unmet_deps"; then
+ echo "WARNING: Disabling engine "`get_engine_name $1`" because the following dependencies are unmet: "$unmet_deps
+ engine_disable $1
+ fi
+ fi
+}
+
# Show the configure help line for a given engine
show_engine_help() {
name=`get_engine_name $1`
@@ -574,27 +660,37 @@ prepare_engine_build_strings() {
# Get the string about building an engine
get_engine_build_string() {
+ engine=$1
+ request_status=$2
engine_string=""
engine_build=`get_engine_build $1`
- engine_build_default=`get_engine_build_default $1`
+ engine_build_default=`get_engine_build_default $engine`
show=no
+ # Convert static/dynamic to yes to ease the check of subengines
+ if test $engine_build = no; then
+ subengine_filter=no
+ else
+ subengine_filter=yes
+ fi
+
# Check if the current engine should be shown for the current status
- if test $engine_build = $2 ; then
+ if test $engine_build = $request_status ; then
show=yes
else
# Test for disabled sub-engines
- if test $2 = no ; then
- for subeng in `get_engine_subengines $1` ; do
+ if test $request_status = no ; then
+ for subeng in `get_engine_subengines $engine` ; do
if test `get_engine_build $subeng` = no ; then
- engine_build=no
+ # In this case we to display _disabled_ subengines
+ subengine_filter=no
show=yes
fi
done
fi
# Test for enabled wip sub-engines
- if test $2 = wip ; then
- for subeng in `get_engine_subengines $1` ; do
+ if test $request_status = wip ; then
+ for subeng in `get_engine_subengines $engine` ; do
if test `get_engine_build $subeng` != no -a `get_engine_build_default $subeng` = no ; then
show=yes
fi
@@ -602,85 +698,82 @@ get_engine_build_string() {
fi
fi
- # Convert static/dynamic to yes to ease the check of subengines
- if test $engine_build != no ; then
- engine_build=yes
- fi
# Check if it is a wip engine
- if test "$2" = "wip" -a "$engine_build" != "no" -a "$engine_build_default" = no; then
+ if test "$request_status" = "wip" -a "$engine_build" != "no" -a "$engine_build_default" = no; then
show=yes
fi
# The engine should be shown, build the string
if test $show = yes ; then
- build_string_func=get_${1}_build_string
- if ( type $build_string_func | grep function ) 2> /dev/null > /dev/null ; then
- engine_string=`$build_string_func $1 $engine_build $2`
- else
- engine_string=`get_subengines_build_string $1 $engine_build "" $2`
- fi
-
- engine_string="`get_engine_name $1` $engine_string"
+ engine_string=`get_subengines_build_string $engine $subengine_filter $request_status`
+ engine_string="`get_engine_name $engine` $engine_string"
fi
- echo $engine_string
+ echo "$engine_string"
}
# Get the string about building subengines
get_subengines_build_string() {
- all=yes
parent_engine=$1
- subengine_string=$3
- parent_status=$4
+ subengine_filter=$2
+ request_status=$3
parent_engine_build_default=`get_engine_build_default $parent_engine`
+ subengine_string=""
- for subeng in `get_engine_subengines $parent_engine` ; do
- subengine_build=`get_engine_build $subeng`
- subengine_build_default=`get_engine_build_default $subeng`
- if test \( $subengine_build = $2 -a "$parent_status" != wip \) -o \( "$parent_status" = wip -a $subengine_build != no -a "$subengine_build_default" = no \) ; then
- subengine_string="$subengine_string [`get_engine_name $subeng`]"
- else
+ # If the base engine isn't built at all, no need to list subengines
+ # in any of the possible categories.
+ if test `get_engine_build $parent_engine` = no; then
+ return
+ fi
+
+ all=yes
+ # If there are no subengines, never display "[all games]" (for brevity).
+ if test -z "`get_engine_subengines $parent_engine`"; then
+ all=no
+ fi
+ # If the base engine does not fit the category we're displaying here
+ # (WIP or Skipped), we should never show "[all games]"
+ if test "$request_status" = wip; then
+ if test $parent_engine_build_default = yes; then
all=no
fi
+ fi
+ if test "$request_status" = no; then
+ # If we're here, the parent engine is built, so no need to check that.
+ all=no
+ fi
- # handle engines that are on by default and have a single subengine that is off by default
- if test "$parent_status" = wip ; then
- if test $parent_engine_build_default = yes -a subengine ; then
- all=no
- fi
- fi
- done
-
- if test $2 != no ; then
- if test -n "$subengine_string" ; then
- if test $all = yes ; then
- subengine_string="[all games]"
- fi
- fi
+ # In the static/dynamic categories, also display the engine's base games.
+ if test -n "`get_engine_subengines $parent_engine`" -a $request_status != no -a $request_status != wip; then
+ subengine_string="[`get_engine_base $parent_engine`]"
fi
- echo $subengine_string
-}
+ for subeng in `get_engine_subengines $parent_engine` ; do
+ subengine_build=`get_engine_build $subeng`
+ subengine_build_default=`get_engine_build_default $subeng`
-# Engine specific build strings
-get_scumm_build_string() {
- if test `get_engine_build $1` != no ; then
- if test $2 != no -a "$3" != wip ; then
- base="[v0-v6 games]"
+ # Display this subengine if it matches the filter, unless it is
+ # a stable subengine in the WIP request.
+ if test $subengine_build = $subengine_filter -a \! \( "$request_status" = wip -a "$subengine_build_default" = yes \) ; then
+ s="[`get_engine_name $subeng`]"
+ if test -n "$subengine_string"; then
+ subengine_string="$subengine_string $s"
+ else
+ subengine_string="$s"
+ fi
+ else
+ all=no
fi
- get_subengines_build_string $1 $2 "$base" $3
- fi
-}
+ done
-get_saga_build_string() {
- if test `get_engine_build $1` != no ; then
- if test $2 != no -a "$3" != wip; then
- base="[ITE]"
- fi
- get_subengines_build_string $1 $2 "$base" $3
+ # Summarize the full list, where applicable
+ if test $all = yes ; then
+ subengine_string="[all games]"
fi
+
+ echo "$subengine_string"
}
#
@@ -1839,7 +1932,9 @@ case $_host_cpu in
define_in_config_if_yes yes 'USE_ARM_SOUND_ASM'
define_in_config_if_yes yes 'USE_ARM_SMUSH_ASM'
define_in_config_if_yes yes 'USE_ARM_GFX_ASM'
- define_in_config_if_yes yes 'USE_ARM_COSTUME_ASM'
+ # FIXME: The following feature exhibits a bug during the intro scene of Indy 4
+ # (on Pandora and iPhone at least)
+ #define_in_config_if_yes yes 'USE_ARM_COSTUME_ASM'
DEFINES="$DEFINES -DARM_TARGET"
;;
@@ -3202,11 +3297,6 @@ fi
define_in_config_if_yes "$_png" 'USE_PNG'
echo "$_png"
-if test `get_engine_build sword25` = yes && test ! "$_png" = yes ; then
- echo "...disabling Broken Sword 2.5 engine. PNG is required"
- engine_disable sword25
-fi
-
#
# Check for Theora Decoder
#
@@ -3317,11 +3407,6 @@ fi
define_in_config_if_yes "$_zlib" 'USE_ZLIB'
echo "$_zlib"
-if test `get_engine_build sword25` = yes && test ! "$_zlib" = yes ; then
- echo "...disabling Broken Sword 2.5 engine. ZLib is required"
- engine_disable sword25
-fi
-
#
# Check for Sparkle if updates support is enabled
#
@@ -3868,6 +3953,9 @@ sh -c "
fi" 2>/dev/null &
for engine in $_engines; do
+ # Check whether all dependencies are available
+ check_engine_deps $engine
+
if test "`get_engine_sub $engine`" = "no" ; then
# It's a main engine
if test `get_engine_build $engine` = no ; then
@@ -3893,9 +3981,6 @@ for engine in $_engines; do
isbuilt=STATIC_PLUGIN
fi
fi
-
- # Prepare the information to be shown
- prepare_engine_build_strings $engine
else
# It's a subengine, just say yes or no
if test "`get_engine_build $engine`" = "no" ; then
@@ -3914,6 +3999,14 @@ for engine in $_engines; do
fi
done
+# Prepare the information to be shown
+for engine in $_engines; do
+ if test "`get_engine_sub $engine`" = "no" ; then
+ # It's a main engine
+ prepare_engine_build_strings $engine
+ fi
+done
+
#
# Detection of WIP/unstable engines
#
@@ -3941,28 +4034,28 @@ fi
echo
if test -n "$_engines_built_static" ; then
echo "Engines (builtin):"
- echo $_engines_built_static | sed 's/@/\
+ echo "$_engines_built_static" | sed 's/@/\
/g
s/#/ /g'
fi
if test -n "$_engines_built_dynamic" ; then
echo "Engines (plugins):"
- echo $_engines_built_dynamic | sed 's/@/\
+ echo "$_engines_built_dynamic" | sed 's/@/\
/g
s/#/ /g'
fi
if test -n "$_engines_skipped" ; then
echo "Engines Skipped:"
- echo $_engines_skipped | sed 's/@/\
+ echo "$_engines_skipped" | sed 's/@/\
/g
s/#/ /g'
fi
if test -n "$_engines_built_wip" ; then
echo "WARNING: This ScummVM build contains the following UNSTABLE engines:"
- echo $_engines_built_wip | sed 's/@/\
+ echo "$_engines_built_wip" | sed 's/@/\
/g
s/#/ /g'
fi
diff --git a/devtools/create_project/config.h b/devtools/create_project/config.h
index 20c1391cef..de4703a47d 100644
--- a/devtools/create_project/config.h
+++ b/devtools/create_project/config.h
@@ -28,7 +28,10 @@
#define LIBS_DEFINE "SCUMMVM_LIBS" // Name of the include environment variable
#define REVISION_DEFINE "SCUMMVM_INTERNAL_REVISION"
-//#define ADDITIONAL_LIBRARY ""
-#define NEEDS_RTTI 0
+#define ENABLE_LANGUAGE_EXTENSIONS "" // Comma separated list of projects that need language extensions
+#define DISABLE_EDIT_AND_CONTINUE "tinsel,tony" // Comma separated list of projects that need Edit&Continue to be disabled for co-routine support (the main project is automatically added)
+
+//#define ADDITIONAL_LIBRARY "" // Add a single library to the list of externally linked libraries
+#define NEEDS_RTTI 0 // Enable RTTI globally
#endif // TOOLS_CREATE_PROJECT_CONFIG_H
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index 8499fec400..a8e09ff5eb 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -97,30 +97,6 @@ struct FSNode {
};
typedef std::list<FSNode> FileList;
-
-typedef StringList TokenList;
-
-/**
- * Takes a given input line and creates a list of tokens out of it.
- *
- * A token in this context is separated by whitespaces. A special case
- * are quotation marks though. A string inside quotation marks is treated
- * as single token, even when it contains whitespaces.
- *
- * Thus for example the input:
- * foo bar "1 2 3 4" ScummVM
- * will create a list with the following entries:
- * "foo", "bar", "1 2 3 4", "ScummVM"
- * As you can see the quotation marks will get *removed* too.
- *
- * You can also use this with non-whitespace by passing another separator
- * character (e.g. ',').
- *
- * @param input The text to be tokenized.
- * @param separator The token separator.
- * @return A list of tokens.
- */
-TokenList tokenize(const std::string &input, char separator = ' ');
} // End of anonymous namespace
enum ProjectType {
@@ -526,7 +502,7 @@ int main(int argc, char *argv[]) {
projectWarnings["agos"].push_back("4511");
projectWarnings["dreamweb"].push_back("4355");
-
+
projectWarnings["lure"].push_back("4189");
projectWarnings["lure"].push_back("4355");
@@ -787,6 +763,7 @@ bool parseEngine(const std::string &line, EngineDesc &engine) {
return true;
}
+} // End of anonymous namespace
TokenList tokenize(const std::string &input, char separator) {
TokenList result;
@@ -819,7 +796,6 @@ TokenList tokenize(const std::string &input, char separator) {
return result;
}
-} // End of anonymous namespace
namespace {
const Feature s_features[] = {
diff --git a/devtools/create_project/create_project.h b/devtools/create_project/create_project.h
index b4eda8f8d2..de77793ee7 100644
--- a/devtools/create_project/create_project.h
+++ b/devtools/create_project/create_project.h
@@ -31,6 +31,30 @@
typedef std::list<std::string> StringList;
+typedef StringList TokenList;
+
+/**
+ * Takes a given input line and creates a list of tokens out of it.
+ *
+ * A token in this context is separated by whitespaces. A special case
+ * are quotation marks though. A string inside quotation marks is treated
+ * as single token, even when it contains whitespaces.
+ *
+ * Thus for example the input:
+ * foo bar "1 2 3 4" ScummVM
+ * will create a list with the following entries:
+ * "foo", "bar", "1 2 3 4", "ScummVM"
+ * As you can see the quotation marks will get *removed* too.
+ *
+ * You can also use this with non-whitespace by passing another separator
+ * character (e.g. ',').
+ *
+ * @param input The text to be tokenized.
+ * @param separator The token separator.
+ * @return A list of tokens.
+ */
+TokenList tokenize(const std::string &input, char separator = ' ');
+
/**
* Structure to describe a game engine to be built into ScummVM.
*
diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp
index c797770955..0f77d91852 100644
--- a/devtools/create_project/msbuild.cpp
+++ b/devtools/create_project/msbuild.cpp
@@ -241,9 +241,11 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s
// Check for project-specific warnings:
std::map<std::string, StringList>::iterator warningsIterator = _projectWarnings.find(name);
+ bool enableLanguageExtensions = find(_enableLanguageExtensions.begin(), _enableLanguageExtensions.end(), name) != _enableLanguageExtensions.end();
+ bool disableEditAndContinue = find(_disableEditAndContinue.begin(), _disableEditAndContinue.end(), name) != _disableEditAndContinue.end();
// Nothing to add here, move along!
- if (!setup.devTools && name != setup.projectName && name != "sword25" && name != "scummvm" && name != "grim" && warningsIterator == _projectWarnings.end())
+ if (!setup.devTools && name != setup.projectName && !enableLanguageExtensions && !disableEditAndContinue && warningsIterator == _projectWarnings.end())
return;
std::string warnings = "";
@@ -254,16 +256,17 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s
project << "\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='" << configuration << "|" << (isWin32 ? "Win32" : "x64") << "'\">\n"
"\t\t<ClCompile>\n";
- // Compile configuration
- if (setup.devTools || name == setup.projectName || name == "sword25" || name == "grim") {
+ // Language Extensions
+ if (setup.devTools || name == setup.projectName || enableLanguageExtensions)
project << "\t\t\t<DisableLanguageExtensions>false</DisableLanguageExtensions>\n";
- if (name == setup.projectName && !isRelease)
- project << "\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\n";
- } else {
- if (warningsIterator != _projectWarnings.end())
- project << "\t\t\t<DisableSpecificWarnings>" << warnings << ";%(DisableSpecificWarnings)</DisableSpecificWarnings>\n";
- }
+ // Edit and Continue
+ if ((name == setup.projectName || disableEditAndContinue) && !isRelease)
+ project << "\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\n";
+
+ // Warnings
+ if (warningsIterator != _projectWarnings.end())
+ project << "\t\t\t<DisableSpecificWarnings>" << warnings << ";%(DisableSpecificWarnings)</DisableSpecificWarnings>\n";
project << "\t\t</ClCompile>\n";
diff --git a/devtools/create_project/msvc.cpp b/devtools/create_project/msvc.cpp
index 96eaf643d1..b8d2401af9 100644
--- a/devtools/create_project/msvc.cpp
+++ b/devtools/create_project/msvc.cpp
@@ -33,6 +33,9 @@ namespace CreateProjectTool {
//////////////////////////////////////////////////////////////////////////
MSVCProvider::MSVCProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version)
: ProjectProvider(global_warnings, project_warnings, version) {
+
+ _enableLanguageExtensions = tokenize(ENABLE_LANGUAGE_EXTENSIONS, ',');
+ _disableEditAndContinue = tokenize(DISABLE_EDIT_AND_CONTINUE, ',');
}
void MSVCProvider::createWorkspace(const BuildSetup &setup) {
@@ -75,10 +78,10 @@ void MSVCProvider::createWorkspace(const BuildSetup &setup) {
solution << "Global\n"
"\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n"
"\t\tDebug|Win32 = Debug|Win32\n"
- "\t\tAnalysis|Win32 = Analysis|Win32\n"
+ "\t\tAnalysis|Win32 = Analysis|Win32\n"
"\t\tRelease|Win32 = Release|Win32\n"
"\t\tDebug|x64 = Debug|x64\n"
- "\t\tAnalysis|x64 = Analysis|x64\n"
+ "\t\tAnalysis|x64 = Analysis|x64\n"
"\t\tRelease|x64 = Release|x64\n"
"\tEndGlobalSection\n"
"\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n";
@@ -86,14 +89,14 @@ void MSVCProvider::createWorkspace(const BuildSetup &setup) {
for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) {
solution << "\t\t{" << i->second << "}.Debug|Win32.ActiveCfg = Debug|Win32\n"
"\t\t{" << i->second << "}.Debug|Win32.Build.0 = Debug|Win32\n"
- "\t\t{" << i->second << "}.Analysis|Win32.ActiveCfg = Analysis|Win32\n"
- "\t\t{" << i->second << "}.Analysis|Win32.Build.0 = Analysis|Win32\n"
+ "\t\t{" << i->second << "}.Analysis|Win32.ActiveCfg = Analysis|Win32\n"
+ "\t\t{" << i->second << "}.Analysis|Win32.Build.0 = Analysis|Win32\n"
"\t\t{" << i->second << "}.Release|Win32.ActiveCfg = Release|Win32\n"
"\t\t{" << i->second << "}.Release|Win32.Build.0 = Release|Win32\n"
"\t\t{" << i->second << "}.Debug|x64.ActiveCfg = Debug|x64\n"
"\t\t{" << i->second << "}.Debug|x64.Build.0 = Debug|x64\n"
- "\t\t{" << i->second << "}.Analysis|x64.ActiveCfg = Analysis|x64\n"
- "\t\t{" << i->second << "}.Analysis|x64.Build.0 = Analysis|x64\n"
+ "\t\t{" << i->second << "}.Analysis|x64.ActiveCfg = Analysis|x64\n"
+ "\t\t{" << i->second << "}.Analysis|x64.Build.0 = Analysis|x64\n"
"\t\t{" << i->second << "}.Release|x64.ActiveCfg = Release|x64\n"
"\t\t{" << i->second << "}.Release|x64.Build.0 = Release|x64\n";
}
@@ -139,7 +142,7 @@ void MSVCProvider::createGlobalProp(const BuildSetup &setup) {
StringList x64EngineDefines = getEngineDefines(setup.engines);
x64Defines.splice(x64Defines.end(), x64EngineDefines);
- // HACK: This definitly should not be here, but otherwise we would not define SDL_BACKEND for x64.
+ // HACK: This definitely should not be here, but otherwise we would not define SDL_BACKEND for x64.
x64Defines.push_back("WIN32");
x64Defines.push_back("SDL_BACKEND");
@@ -168,7 +171,7 @@ std::string MSVCProvider::getPostBuildEvent(bool isWin32, bool createInstaller)
cmdLine += (isWin32) ? "x86" : "x64";
- cmdLine += " %SCUMMVM_LIBS% ";
+ cmdLine += " %" LIBS_DEFINE "% ";
// Specify if installer needs to be built or not
cmdLine += (createInstaller ? "1" : "0");
diff --git a/devtools/create_project/msvc.h b/devtools/create_project/msvc.h
index 0a994667fa..5a854b596a 100644
--- a/devtools/create_project/msvc.h
+++ b/devtools/create_project/msvc.h
@@ -32,6 +32,8 @@ public:
MSVCProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version);
protected:
+ StringList _enableLanguageExtensions;
+ StringList _disableEditAndContinue;
void createWorkspace(const BuildSetup &setup);
diff --git a/devtools/create_project/scripts/postbuild.cmd b/devtools/create_project/scripts/postbuild.cmd
index d78119d058..8b70ec3dd8 100644
--- a/devtools/create_project/scripts/postbuild.cmd
+++ b/devtools/create_project/scripts/postbuild.cmd
@@ -24,8 +24,10 @@ echo Copying data files
echo.
xcopy /F /Y "%~4/lib/%~3/SDL.dll" "%~2" 1>NUL 2>&1
-xcopy /F /Y "%~4/lib/%~3/freetype6.dll" "%~2" 1>NUL 2>&1
+xcopy /F /Y "%~4/lib/%~3/freetype6.dll" "%~2" 1>NUL 2>&1
xcopy /F /Y "%~1/backends/vkeybd/packs/vkeybd_default.zip" "%~2" 1>NUL 2>&1
+xcopy /F /Y "%~1/gui/themes/translations.dat" "%~2" 1>NUL 2>&1
+
if "%~5"=="0" goto done
diff --git a/devtools/create_project/visualstudio.cpp b/devtools/create_project/visualstudio.cpp
index c301e78ad1..de2df96d78 100644
--- a/devtools/create_project/visualstudio.cpp
+++ b/devtools/create_project/visualstudio.cpp
@@ -103,6 +103,9 @@ void VisualStudioProvider::createProjectFile(const std::string &name, const std:
outputConfiguration(project, setup, libraries, "Release", "x64", "64", false);
} else {
+ bool enableLanguageExtensions = find(_enableLanguageExtensions.begin(), _enableLanguageExtensions.end(), name) != _enableLanguageExtensions.end();
+ bool disableEditAndContinue = find(_disableEditAndContinue.begin(), _disableEditAndContinue.end(), name) != _disableEditAndContinue.end();
+
std::string warnings = "";
if (warningsIterator != _projectWarnings.end())
for (StringList::const_iterator i = warningsIterator->second.begin(); i != warningsIterator->second.end(); ++i)
@@ -110,9 +113,8 @@ void VisualStudioProvider::createProjectFile(const std::string &name, const std:
std::string toolConfig;
toolConfig = (!warnings.empty() ? "DisableSpecificWarnings=\"" + warnings + "\"" : "");
- toolConfig += (name == setup.projectName ? "DebugInformationFormat=\"3\" " : "");
- toolConfig += (name == "sword25" ? "DisableLanguageExtensions=\"false\" " : "");
- toolConfig += (name == "grim" ? "DisableLanguageExtensions=\"false\" " : "");
+ toolConfig += (disableEditAndContinue ? "DebugInformationFormat=\"3\" " : "");
+ toolConfig += (enableLanguageExtensions ? "DisableLanguageExtensions=\"false\" " : "");
// Win32
outputConfiguration(setup, project, toolConfig, "Debug", "Win32", "");
diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp
index 62dd417d8c..a9b8e7a752 100644
--- a/devtools/create_project/xcode.cpp
+++ b/devtools/create_project/xcode.cpp
@@ -206,7 +206,7 @@ void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &p
// Create group
std::string name = getLastPathComponent(dir.name);
Object *group = new Object(this, "PBXGroup_" + name , "PBXGroup", "PBXGroup", "", name);
-
+
// List of children
Property children;
children.hasOrder = true;
@@ -225,7 +225,7 @@ void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &p
ADD_SETTING_ORDER_NOVALUE(children, getHash(id), node->name, order++);
ADD_BUILD_FILE(id, node->name, node->name + " in Sources");
ADD_FILE_REFERENCE(node->name, property);
-
+
// Process child nodes
if (!node->children.empty())
writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/');
diff --git a/devtools/create_tony/create_tony.cpp b/devtools/create_tony/create_tony.cpp
new file mode 100644
index 0000000000..f2d086c083
--- /dev/null
+++ b/devtools/create_tony/create_tony.cpp
@@ -0,0 +1,183 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * This is a utility for storing all the hardcoded data of Tony Tough in a separate
+ * data file, used by the game engine
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+// HACK to allow building with the SDL backend on MinGW
+// see bug #1800764 "TOOLS: MinGW tools building broken"
+#ifdef main
+#undef main
+#endif // main
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/scummsys.h"
+#include "common/events.h"
+
+#include "create_tony.h"
+#include "staticdata.h"
+
+static void writeByte(FILE *fp, uint8 b) {
+ fwrite(&b, 1, 1, fp);
+}
+
+static void writeUint16BE(FILE *fp, uint16 value) {
+ writeByte(fp, (uint8)(value >> 8));
+ writeByte(fp, (uint8)(value & 0xFF));
+}
+
+void writeSint16BE(FILE *fp, int16 value) {
+ writeUint16BE(fp, (uint16)value);
+}
+
+static void writeUint32BE(FILE *fp, uint32 value) {
+ writeByte(fp, (uint8)(value >> 24));
+ writeByte(fp, (uint8)((value >> 16) & 0xFF));
+ writeByte(fp, (uint8)((value >> 8) & 0xFF));
+ writeByte(fp, (uint8)(value & 0xFF));
+}
+
+void writeSint32BE(FILE *fp, int32 value) {
+ writeUint32BE(fp, (uint16)value);
+}
+
+int main(int argc, char *argv[]) {
+ FILE *outFile;
+
+ outFile = fopen("tony.dat", "wb");
+
+ // Write header
+ fwrite("TONY", 4, 1, outFile);
+
+ writeByte(outFile, TONY_DAT_VER_MAJ);
+ writeByte(outFile, TONY_DAT_VER_MIN);
+
+ // game versions/variants
+ writeUint16BE(outFile, NUM_VARIANTS);
+
+ // Italian
+ for (int i = 0; i < 256; i++) {
+ writeSint16BE(outFile, _cTableDialogIta[i]);
+ writeSint16BE(outFile, _lTableDialogIta[i]);
+ writeSint16BE(outFile, _cTableMaccIta[i]);
+ writeSint16BE(outFile, _lTableMaccIta[i]);
+ writeSint16BE(outFile, _cTableCredIta[i]);
+ writeSint16BE(outFile, _lTableCredIta[i]);
+ writeSint16BE(outFile, _cTableObjIta[i]);
+ writeSint16BE(outFile, _lTableObjIta[i]);
+ }
+
+ // Polish
+ for (int i = 0; i < 256; i++) {
+ writeSint16BE(outFile, _cTableDialogPol[i]);
+ writeSint16BE(outFile, _lTableDialogPol[i]);
+ writeSint16BE(outFile, _cTableMaccPol[i]);
+ writeSint16BE(outFile, _lTableMaccPol[i]);
+ writeSint16BE(outFile, _cTableCredPol[i]);
+ writeSint16BE(outFile, _lTableCredPol[i]);
+ writeSint16BE(outFile, _cTableObjPol[i]);
+ writeSint16BE(outFile, _lTableObjPol[i]);
+ }
+
+ //Russian
+ for (int i = 0; i < 256; i++) {
+ writeSint16BE(outFile, _cTableDialogRus[i]);
+ writeSint16BE(outFile, _lTableDialogRus[i]);
+ writeSint16BE(outFile, _cTableMaccRus[i]);
+ writeSint16BE(outFile, _lTableMaccRus[i]);
+ writeSint16BE(outFile, _cTableCredRus[i]);
+ writeSint16BE(outFile, _lTableCredRus[i]);
+ writeSint16BE(outFile, _cTableObjRus[i]);
+ writeSint16BE(outFile, _lTableObjRus[i]);
+ }
+
+ // Czech
+ for (int i = 0; i < 256; i++) {
+ writeSint16BE(outFile, _cTableDialogCze[i]);
+ writeSint16BE(outFile, _lTableDialogCze[i]);
+ writeSint16BE(outFile, _cTableMaccCze[i]);
+ writeSint16BE(outFile, _lTableMaccCze[i]);
+ writeSint16BE(outFile, _cTableCredCze[i]);
+ writeSint16BE(outFile, _lTableCredCze[i]);
+ writeSint16BE(outFile, _cTableObjCze[i]);
+ writeSint16BE(outFile, _lTableObjCze[i]);
+ }
+
+ // French
+ for (int i = 0; i < 256; i++) {
+ writeSint16BE(outFile, _cTableDialogFra[i]);
+ writeSint16BE(outFile, _lTableDialogFra[i]);
+ writeSint16BE(outFile, _cTableMaccFra[i]);
+ writeSint16BE(outFile, _lTableMaccFra[i]);
+ writeSint16BE(outFile, _cTableCredFra[i]);
+ writeSint16BE(outFile, _lTableCredFra[i]);
+ writeSint16BE(outFile, _cTableObjFra[i]);
+ writeSint16BE(outFile, _lTableObjFra[i]);
+ }
+
+ // Deutsch
+ for (int i = 0; i < 256; i++) {
+ writeSint16BE(outFile, _cTableDialogDeu[i]);
+ writeSint16BE(outFile, _lTableDialogDeu[i]);
+ writeSint16BE(outFile, _cTableMaccDeu[i]);
+ writeSint16BE(outFile, _lTableMaccDeu[i]);
+ writeSint16BE(outFile, _cTableCredDeu[i]);
+ writeSint16BE(outFile, _lTableCredDeu[i]);
+ writeSint16BE(outFile, _cTableObjDeu[i]);
+ writeSint16BE(outFile, _lTableObjDeu[i]);
+ }
+
+ fclose(outFile);
+ return 0;
+}
+
+void writeTextArray(FILE *outFile, const char *textArray[], int nbrText) {
+ int len, len1, pad;
+ uint8 padBuf[DATAALIGNMENT];
+
+ for (int i = 0; i < DATAALIGNMENT; i++)
+ padBuf[i] = 0;
+
+ writeUint16BE(outFile, nbrText);
+ len = DATAALIGNMENT - 2;
+ for (int i = 0; i < nbrText; i++) {
+ len1 = strlen(textArray[i]) + 1;
+ pad = DATAALIGNMENT - (len1 + 2) % DATAALIGNMENT;
+ len += 2 + len1 + pad;
+ }
+ writeUint16BE(outFile, len);
+
+ fwrite(padBuf, DATAALIGNMENT - 2, 1, outFile); // padding
+ for (int i = 0; i < nbrText; i++) {
+ len = strlen(textArray[i]) + 1;
+ pad = DATAALIGNMENT - (len + 2) % DATAALIGNMENT;
+
+ writeUint16BE(outFile, len + pad + 2);
+ fwrite(textArray[i], len, 1, outFile);
+ fwrite(padBuf, pad, 1, outFile);
+ }
+}
diff --git a/devtools/create_tony/create_tony.h b/devtools/create_tony/create_tony.h
new file mode 100644
index 0000000000..3075835bd9
--- /dev/null
+++ b/devtools/create_tony/create_tony.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef CREATE_TONY_H
+#define CREATE_TONY_H
+
+#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
+
+#define DATAALIGNMENT 4
+
+#define TONY_DAT_VER_MAJ 0 // 1 byte
+#define TONY_DAT_VER_MIN 3 // 1 byte
+
+// Number of variants of the game. For the moment, it's the same
+// as the number of languages
+#define NUM_VARIANTS 6
+
+typedef unsigned char uint8;
+typedef unsigned char byte;
+typedef unsigned short uint16;
+typedef signed short int16;
+
+void writeTextArray(FILE *outFile, const char *textData[], int nbrText);
+
+#endif // CREATE_TONY_H
diff --git a/devtools/create_tony/module.mk b/devtools/create_tony/module.mk
new file mode 100644
index 0000000000..6ff919032b
--- /dev/null
+++ b/devtools/create_tony/module.mk
@@ -0,0 +1,10 @@
+MODULE := devtools/create_tony
+
+MODULE_OBJS := \
+ create_tony.o
+
+# Set the name of the executable
+TOOL_EXECUTABLE := create_tony
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/devtools/create_tony/staticdata.h b/devtools/create_tony/staticdata.h
new file mode 100644
index 0000000000..fff977d8dc
--- /dev/null
+++ b/devtools/create_tony/staticdata.h
@@ -0,0 +1,1370 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef STATICDATA_H
+#define STATICDATA_H
+
+const int _cTableDialogIta[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 71, 77, -1, 80, 81, 82, 111,
+ 75, 76, -1, 68, 63, 66, 64, 78, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 65, 62,
+ 69, 83, 70, 73, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 77, 67, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 79, -1, -1, -1, -1, -1, 87,
+ -1, 84, -1, -1, 86, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 85, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 108, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 105,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 88, -1, -1, -1, 93, 99,
+ 107, 106, 89, 89, -1, 94, 90, -1, -1, 95,
+ -1, 104, 91, -1, -1, -1, 96, -1, 109, 92,
+ -1, -1, 97, -1, -1, 98};
+
+const int _lTableDialogIta[] = {
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 9, 5, 5, 13, 13, 13, 13, 5,
+ 7, 7, 13, 13, 5, 13, 5, 13, 13, 13,
+ 13, 13, 10, 13, 13, 13, 13, 13, 5, 5,
+ 13, 13, 13, 10, 13, 13, 13, 13, 13, 10,
+ 11, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 5, 13, 13, 14, 15, 12,
+ 13, 12, 13, 13, 13, 6, 13, 13, 5, 16,
+ 12, 11, 11, 13, 13, 12, 13, 12, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13};
+
+const int _cTableDialogPol[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 71, 77, -1, 80, 81, 82, 111,
+ 75, 76, -1, 68, 63, 66, 64, 78, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 65, 62,
+ 69, 83, 70, 73, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 77, 67, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 124, -1, -1, 128, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 125, -1, -1, 129,
+ -1, -1, -1, 118, -1, 112, -1, -1, -1, 87,
+ -1, 84, -1, -1, 86, 126, -1, -1, -1, 119,
+ -1, -1, -1, -1, -1, 113, -1, 85, -1, -1,
+ -1, 127, -1, -1, -1, -1, -1, -1, 114, -1,
+ -1, -1, 116, -1, -1, -1, -1, -1, -1, 120,
+ -1, 122, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 88, -1, -1, -1, 93, 99,
+ 115, 106, 89, 89, 117, 94, 90, -1, -1, 95,
+ -1, 121, 91, 123, -1, -1, 96, -1, 109, 92,
+ -1, -1, 97, -1, -1, 98};
+
+const int _lTableDialogPol[] = {
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 9, 5, 5, 13, 13, 13, 13, 5,
+ 7, 7, 13, 13, 5, 13, 5, 13, 13, 13,
+ 13, 13, 10, 13, 13, 13, 13, 13, 5, 5,
+ 13, 13, 13, 10, 13, 13, 13, 13, 13, 10,
+ 11, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 5, 13, 13, 14, 15, 12,
+ 13, 12, 13, 13, 13, 6, 13, 13, 5, 16,
+ 12, 11, 11, 13, 13, 12, 13, 12, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 12, 13, 13, 14, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 12, 13, 13, 13,
+ 13, 13, 13, 14, 13, 14, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 9,
+ 13, 13, 13, 13, 13, 16, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 12, 13,
+ 13, 13, 11, 13, 13, 13, 13, 13, 13, 10,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 12, 13, 13, 13, 12, 13, 13, 13, 13, 13,
+ 13, 11, 13, 11, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13};
+
+const int _cTableDialogRus[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 71, 77, -1, 80, 81, 82, 111,
+ 75, 76, -1, 68, 63, 66, 64, 78, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 65, 62,
+ 69, 83, 70, 73, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 77, 67, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 79, -1, -1, -1, -1, 136, 87,
+ -1, 84, -1, -1, 86, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 169, -1, -1, 85, -1, -1,
+ -1, -1, 130, 131, 132, 133, 134, 135, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 158, 159,
+ 157, 160, 161, 162, 163, 164, 165, 166, 167, 168,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 191, 192, 190, 193, 194, 195};
+
+const int _lTableDialogRus[] = {
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 9, 5, 5, 13, 13, 13, 13, 5,
+ 7, 7, 13, 13, 5, 13, 5, 13, 13, 13,
+ 13, 13, 10, 13, 13, 13, 13, 13, 5, 5,
+ 13, 13, 13, 10, 13, 13, 13, 13, 13, 10,
+ 11, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 5, 13, 13, 14, 15, 12,
+ 13, 12, 13, 13, 13, 6, 13, 13, 5, 16,
+ 12, 11, 11, 13, 13, 12, 13, 12, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 11, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 12, 13, 13, 13, 13, 13,
+ 13, 13, 13, 15, 15, 11, 15, 11, 15, 10,
+ 13, 13, 12, 13, 14, 14, 13, 11, 12, 12,
+ 18, 11, 13, 12, 13, 12, 17, 18, 18, 19,
+ 16, 11, 16, 14, 14, 15, 10, 12, 13, 12,
+ 12, 10, 10, 10, 11, 12, 12, 12, 12, 10,
+ 11, 10, 14, 8, 11, 11, 12, 10, 15, 16,
+ 16, 16, 14, 9, 15, 14};
+
+const int _cTableDialogCze[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 71, 77, -1, 80, 81, 82, 111,
+ 75, 76, -1, 68, 63, 66, 64, 78, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 65, 62,
+ 69, 83, 70, 73, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 77, 67, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 197, -1,
+ -1, 206, 200, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 212, -1, -1, 221, 215, -1,
+ -1, -1, -1, 79, -1, -1, -1, -1, -1, 87,
+ -1, 84, -1, -1, 86, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 85, -1, -1,
+ -1, -1, -1, 202, -1, -1, -1, -1, 108, -1,
+ 198, 204, -1, -1, 196, 203, -1, 205, -1, 105,
+ 207, 208, -1, -1, -1, -1, 199, 209, 210, -1,
+ -1, 201, -1, -1, 88, 217, -1, -1, 93, 99,
+ 107, 106, 213, 219, -1, 94, 211, 218, -1, 220,
+ -1, 104, 222, 223, -1, -1, 96, -1, 214, 224,
+ 225, -1, 97, 216, -1, 98};
+
+const int _lTableDialogCze[] = {
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 9, 5, 5, 13, 13, 13, 13, 5,
+ 7, 7, 13, 13, 5, 13, 5, 13, 13, 13,
+ 13, 13, 10, 13, 13, 13, 13, 13, 5, 5,
+ 13, 13, 13, 10, 13, 13, 13, 13, 13, 10,
+ 11, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 5, 13, 13, 14, 15, 12,
+ 13, 12, 13, 13, 13, 6, 13, 13, 5, 16,
+ 12, 11, 11, 13, 13, 12, 13, 12, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 12, 13,
+ 13, 19, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 12, 13, 13, 16, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 12, 11, 13, 13, 11, 11, 13, 15, 13, 13,
+ 10, 13, 13, 13, 13, 13, 14, 13, 13, 13,
+ 13, 11, 13, 13, 13, 15, 13, 13, 13, 13,
+ 13, 13, 12, 12, 13, 13, 12, 7, 13, 17,
+ 13, 13, 11, 11, 13, 13, 13, 13, 12, 13,
+ 13, 13, 13, 11, 13, 13};
+
+const int _cTableDialogFra[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 71, 77, -1, 80, 81, 82, 111,
+ 75, 76, -1, 68, 63, 66, 64, 78, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 65, 62,
+ 69, 83, 70, 73, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 77, 67, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 79, -1, -1, -1, -1, -1, 87,
+ -1, 84, -1, -1, 86, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 85, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 108, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 105,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 88, -1, 226, -1, 93, 99,
+ 107, 106, 89, 227, 228, 94, 90, -1, 229, 95,
+ -1, 104, 91, -1, 232, -1, 233, -1, 109, 230,
+ -1, 231, 97, -1, -1, 98};
+
+const int _lTableDialogFra[] = {
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 9, 5, 5, 13, 13, 13, 13, 5,
+ 7, 7, 13, 13, 5, 13, 5, 13, 13, 13,
+ 13, 13, 10, 13, 13, 13, 13, 13, 5, 5,
+ 13, 13, 13, 10, 13, 13, 13, 13, 13, 10,
+ 11, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 5, 13, 13, 14, 15, 12,
+ 13, 12, 13, 13, 13, 6, 13, 13, 5, 16,
+ 12, 11, 11, 13, 13, 12, 13, 12, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 15, 13, 13, 13,
+ 13, 13, 13, 12, 12, 13, 13, 13, 9, 13,
+ 13, 13, 13, 13, 11, 13, 11, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13};
+
+const int _cTableDialogDeu[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 71, 77, -1, 80, 81, 82, 111,
+ 75, 76, -1, 68, 63, 66, 64, 78, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 65, 62,
+ 69, 83, 70, 73, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 77, 67, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 79, -1, -1, -1, -1, -1, 87,
+ -1, 84, -1, -1, 86, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 85, -1, -1,
+ -1, -1, -1, -1, -1, -1, 236, -1, 108, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 105,
+ -1, -1, -1, -1, 237, -1, -1, -1, -1, -1,
+ 238, -1, -1, 234, 88, -1, -1, -1, 93, 99,
+ 107, 106, 89, 89, -1, 94, 90, -1, -1, 95,
+ -1, 104, 91, -1, -1, -1, 96, -1, 109, 92,
+ -1, -1, 97, -1, -1, 98};
+
+const int _lTableDialogDeu[] = {
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 9, 5, 5, 13, 13, 13, 13, 5,
+ 7, 7, 13, 13, 5, 13, 5, 13, 13, 13,
+ 13, 13, 10, 13, 13, 13, 13, 13, 5, 5,
+ 13, 13, 13, 10, 13, 13, 13, 13, 13, 10,
+ 11, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 5, 13, 13, 14, 15, 12,
+ 13, 12, 13, 13, 13, 6, 13, 13, 5, 16,
+ 12, 11, 11, 13, 13, 12, 13, 12, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 15, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13};
+
+const int _cTableMaccIta[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, 64, -1, 65, 66, 67, -1,
+ 69, 70, 74, 75, 78, 81, 79, 84, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 80, 77,
+ 82, 71, 83, 72, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 86, -1, -1, -1, 87, 88,
+ -1, 101, 89, -1, -1, 90, 92, -1, -1, 93,
+ -1, 76, 95, -1, -1, -1, 96, -1, -1, 98,
+ -1, -1, 99, -1, -1, 85};
+
+const int _lTableMaccIta[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10};
+
+const int _cTableMaccPol[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, 64, -1, 65, 66, 67, -1,
+ 69, 70, 74, 75, 78, 81, 79, 84, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 80, 77,
+ 82, 71, 83, 72, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 114, -1, -1, 118, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 115, -1, -1, 119,
+ -1, -1, -1, 108, -1, 102, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 116, -1, -1, -1, 109,
+ -1, -1, -1, -1, -1, 103, -1, -1, -1, -1,
+ -1, 117, -1, -1, -1, -1, -1, -1, 104, -1,
+ -1, -1, 106, -1, -1, -1, -1, -1, -1, 110,
+ -1, 112, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 86, -1, -1, -1, 87, 88,
+ 105, 101, 89, -1, 107, 90, 92, -1, -1, 93,
+ -1, 111, 95, 113, -1, -1, 96, -1, -1, 98,
+ -1, -1, 99, -1, -1, 85};
+
+const int _lTableMaccPol[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 12, 10, 10, 14, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 12, 10, 10, 13,
+ 10, 10, 10, 14, 10, 14, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 13, 10, 10, 10, 9,
+ 10, 10, 10, 10, 10, 16, 10, 10, 10, 10,
+ 10, 13, 10, 10, 10, 10, 10, 10, 12, 10,
+ 10, 10, 11, 10, 10, 10, 10, 10, 10, 10,
+ 10, 13, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 12, 10, 10, 10, 12, 10, 10, 10, 10, 10,
+ 10, 11, 10, 11, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10};
+
+const int _cTableMaccRus[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, 64, -1, 65, 66, 67, -1,
+ 69, 70, 74, 75, 78, 81, 79, 84, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 80, 77,
+ 82, 71, 83, 72, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 126, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 159, -1, -1, -1, -1, -1,
+ -1, -1, 120, 121, 122, 123, 124, 125, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 148, 149,
+ 147, 150, 151, 152, 153, 154, 155, 156, 157, 158,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 181, 182, 180, 183, 184, 185};
+
+const int _lTableMaccRus[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 9, 10, 10, 10, 10, 10,
+ 10, 10, 11, 11, 11, 9, 10, 10, 11, 10,
+ 10, 10, 11, 9, 11, 10, 11, 8, 10, 10,
+ 11, 11, 11, 11, 10, 10, 10, 10, 11, 11,
+ 11, 11, 11, 11, 10, 10, 11, 10, 9, 10,
+ 10, 9, 11, 11, 11, 11, 11, 11, 10, 9,
+ 11, 10, 9, 11, 10, 11, 10, 10, 11, 11,
+ 10, 10, 10, 9, 11, 11};
+
+const int _cTableMaccCze[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, 64, -1, 65, 66, 67, -1,
+ 69, 70, 74, 75, 78, 81, 79, 84, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 80, 77,
+ 82, 71, 83, 72, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 187, -1,
+ -1, 196, 190, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 202, -1, -1, 211, 205, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 192, -1, -1, -1, -1, -1, -1,
+ 188, 194, -1, -1, 186, 193, -1, 195, -1, -1,
+ 197, 198, -1, -1, -1, -1, 189, 199, 200, -1,
+ -1, 191, -1, -1, 86, 207, -1, -1, 87, 88,
+ -1, 101, 203, 209, -1, 90, 201, 208, -1, 210,
+ -1, 76, 212, 213, -1, -1, 96, -1, 204, 214,
+ 215, -1, 99, 206, -1, 85};
+
+const int _lTableMaccCze[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 11, 9, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 9, 10, 10, 11, 9, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 11, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 9, 10, 11, 10, 10,
+ 11, 11, 10, 10, 10, 10, 11, 11, 11, 10,
+ 10, 11, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 9, 10, 11,
+ 10, 10, 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 11, 10, 10};
+
+const int _cTableMaccFra[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, 64, -1, 65, 66, 67, -1,
+ 69, 70, 74, 75, 78, 81, 79, 84, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 80, 77,
+ 82, 71, 83, 72, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 86, -1, 226, -1, 87, 88,
+ -1, 101, 228, 227, -1, 90, 92, -1, 229, 93,
+ -1, 76, 95, -1, 232, -1, 233, -1, -1, 230,
+ -1, 231, 99, -1, -1, 85};
+
+const int _lTableMaccFra[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 8, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 11,
+ 10, 11, 10, 10, 10, 10};
+
+const int _cTableMaccDeu[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, 64, -1, 65, 66, 67, -1,
+ 69, 70, 74, 75, 78, 81, 79, 84, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 80, 77,
+ 82, 71, 83, 72, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 236, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 237, -1, -1, -1, -1, -1,
+ 238, -1, -1, 234, 86, -1, -1, -1, 87, 88,
+ -1, 101, 89, -1, -1, 90, 92, -1, -1, 93,
+ -1, 76, 95, -1, -1, -1, 96, -1, -1, 98,
+ -1, -1, 99, -1, -1, 85};
+
+const int _lTableMaccDeu[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 11, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10};
+
+const int _cTableCredIta[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 81, -1, -1, -1, 90, 91, 111,
+ 84, 85, 99, 93, 95, -1, 100, 92, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 89, 94,
+ -1, 97, -1, 79, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 96, 98, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 77,
+ -1, 86, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 87, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 52, 53, -1, -1, -1, -1,
+ -1, 74, 56, 57, -1, -1, 60, 61, -1, -1,
+ -1, 73, 64, 65, -1, -1, -1, -1, -1, 68,
+ 69, -1, -1, -1, -1, -1};
+
+const int _lTableCredIta[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 11, 10, 10, 10, 10, 10, 10, 5,
+ 10, 10, 10, 10, 5, 10, 5, 10, 12, 8,
+ 10, 11, 12, 11, 12, 10, 11, 10, 5, 5,
+ 10, 10, 10, 10, 10, 19, 15, 14, 13, 14,
+ 13, 16, 15, 5, 8, 15, 13, 17, 15, 14,
+ 12, 14, 14, 15, 11, 12, 12, 16, 12, 13,
+ 14, 10, 10, 10, 9, 10, 10, 11, 9, 9,
+ 10, 9, 8, 9, 10, 5, 6, 12, 6, 14,
+ 10, 11, 11, 9, 9, 9, 6, 9, 10, 14,
+ 9, 10, 9, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 19, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10};
+
+const int _cTableCredPol[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 81, -1, -1, -1, 90, 91, 111,
+ 84, 85, 99, 93, 95, -1, 100, 92, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 89, 94,
+ -1, 97, -1, 79, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 96, 98, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 124, -1, -1, 128, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 125, -1, -1, 129,
+ -1, -1, -1, 118, -1, 112, -1, -1, -1, 77,
+ -1, 86, -1, -1, -1, 126, -1, -1, -1, 119,
+ -1, -1, -1, -1, -1, 113, -1, 87, -1, -1,
+ -1, 127, -1, -1, -1, -1, -1, -1, 114, -1,
+ -1, -1, 116, -1, -1, -1, -1, -1, -1, 120,
+ -1, 122, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 52, 53, -1, -1, -1, -1,
+ 115, 74, 56, 57, 117, -1, 60, 61, -1, -1,
+ -1, 121, 64, 123, -1, -1, -1, -1, -1, 68,
+ 69, -1, -1, -1, -1, -1};
+
+const int _lTableCredPol[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 11, 10, 10, 10, 10, 10, 10, 5,
+ 10, 10, 10, 10, 5, 10, 5, 10, 12, 8,
+ 10, 11, 12, 11, 12, 10, 11, 10, 5, 5,
+ 10, 10, 10, 10, 10, 19, 15, 14, 13, 14,
+ 13, 16, 15, 5, 8, 15, 13, 17, 15, 14,
+ 12, 14, 14, 15, 11, 12, 12, 16, 12, 13,
+ 14, 10, 10, 10, 9, 10, 10, 11, 9, 9,
+ 10, 9, 8, 9, 10, 5, 6, 12, 6, 14,
+ 10, 11, 11, 9, 9, 9, 6, 9, 10, 14,
+ 9, 10, 9, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 15, 10, 10, 15, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 14, 10, 20, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 15, 10, 10, 10, 11,
+ 10, 10, 10, 10, 10, 12, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 19, 10, 15, 10,
+ 10, 10, 15, 10, 10, 10, 10, 10, 10, 16,
+ 10, 15, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 11, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10};
+
+const int _cTableCredRus[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 81, -1, -1, -1, 90, 91, 111,
+ 84, 85, 99, 93, 95, -1, 100, 92, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 89, 94,
+ -1, 97, -1, 79, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 96, 98, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 136, 77,
+ -1, 86, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 169, -1, -1, 87, -1, -1,
+ -1, -1, 130, 131, 132, 133, 134, 135, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 158, 159,
+ 157, 160, 161, 162, 163, 164, 165, 166, 167, 168,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 191, 192, 190, 193, 194, 195};
+
+const int _lTableCredRus[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 11, 10, 10, 10, 10, 10, 10, 5,
+ 10, 10, 10, 10, 5, 10, 5, 10, 12, 8,
+ 10, 11, 12, 11, 12, 10, 11, 10, 5, 5,
+ 10, 10, 10, 10, 10, 19, 15, 14, 13, 14,
+ 13, 16, 15, 5, 8, 15, 13, 17, 15, 14,
+ 12, 14, 14, 15, 11, 12, 12, 16, 12, 13,
+ 14, 10, 10, 10, 9, 10, 10, 11, 9, 9,
+ 10, 9, 8, 9, 10, 5, 6, 12, 6, 14,
+ 10, 11, 11, 9, 9, 9, 6, 9, 10, 14,
+ 9, 10, 9, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 15, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 11, 10, 10, 10, 10, 10,
+ 10, 10, 20, 16, 16, 14, 22, 15, 20, 12,
+ 16, 16, 16, 22, 18, 16, 15, 14, 13, 15,
+ 12, 14, 15, 13, 16, 14, 23, 23, 12, 16,
+ 10, 12, 20, 15, 12, 10, 10, 11, 16, 10,
+ 13, 12, 13, 13, 12, 13, 14, 11, 11, 11,
+ 12, 10, 10, 10, 11, 10, 11, 10, 15, 15,
+ 12, 16, 10, 11, 13, 11};
+
+const int _cTableCredCze[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 81, -1, -1, -1, 90, 91, 111,
+ 84, 85, 99, 93, 95, -1, 100, 92, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 89, 94,
+ -1, 97, -1, 79, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 96, 98, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 197, -1,
+ -1, 206, 200, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 212, -1, -1, 221, 215, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 77,
+ -1, 86, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 87, -1, -1,
+ -1, -1, -1, 202, -1, -1, -1, -1, -1, -1,
+ 198, 204, -1, -1, 196, 203, -1, 205, -1, -1,
+ 207, 208, -1, -1, -1, -1, 199, 209, 210, -1,
+ -1, 201, -1, -1, 52, 217, -1, -1, -1, -1,
+ -1, 74, 213, 219, -1, -1, 211, 218, -1, 220,
+ -1, 73, 222, 223, -1, -1, -1, -1, 214, 224,
+ 225, -1, -1, 216, -1, -1};
+
+const int _lTableCredCze[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 11, 10, 10, 10, 10, 10, 10, 5,
+ 10, 10, 10, 10, 5, 10, 5, 10, 12, 8,
+ 10, 11, 12, 11, 12, 10, 11, 10, 5, 5,
+ 10, 10, 10, 10, 10, 19, 15, 14, 13, 14,
+ 13, 16, 15, 5, 8, 15, 13, 17, 15, 14,
+ 12, 14, 14, 15, 11, 12, 12, 16, 12, 13,
+ 14, 10, 10, 10, 9, 10, 10, 11, 9, 9,
+ 10, 9, 8, 9, 10, 5, 6, 12, 6, 14,
+ 10, 11, 11, 9, 9, 9, 6, 9, 10, 14,
+ 9, 10, 9, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 15, 10,
+ 10, 19, 15, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 11, 10, 10, 12, 11, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 20, 10, 10, 19, 10, 10, 10,
+ 15, 15, 10, 10, 15, 7, 10, 20, 10, 10,
+ 16, 15, 10, 10, 10, 10, 15, 13, 13, 10,
+ 10, 14, 10, 10, 10, 12, 10, 10, 10, 10,
+ 10, 10, 11, 10, 10, 10, 11, 6, 10, 15,
+ 10, 10, 11, 11, 10, 10, 10, 10, 11, 10,
+ 10, 10, 10, 10, 10, 10};
+
+const int _cTableCredFra[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 81, -1, -1, -1, 90, 91, 111,
+ 84, 85, 99, 93, 95, -1, 100, 92, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 89, 94,
+ -1, 97, -1, 79, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 96, 98, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 77,
+ -1, 86, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 87, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 52, 53, 226, -1, -1, -1,
+ -1, 74, 56, 227, 228, -1, 60, 61, 229, -1,
+ -1, 73, 64, 65, 232, -1, 233, -1, -1, 230,
+ 69, 231, -1, -1, -1, -1};
+
+const int _lTableCredFra[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 11, 10, 10, 10, 10, 10, 10, 5,
+ 10, 10, 10, 10, 5, 10, 5, 10, 12, 8,
+ 10, 11, 12, 11, 12, 10, 11, 10, 5, 5,
+ 10, 10, 10, 10, 10, 19, 15, 14, 13, 14,
+ 13, 16, 15, 5, 8, 15, 13, 17, 15, 14,
+ 12, 14, 14, 15, 11, 12, 12, 16, 12, 13,
+ 14, 10, 10, 10, 9, 10, 10, 11, 9, 9,
+ 10, 9, 8, 9, 10, 5, 6, 12, 6, 14,
+ 10, 11, 11, 9, 9, 9, 6, 9, 10, 14,
+ 9, 10, 9, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 19, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 12, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 6, 10,
+ 10, 10, 10, 10, 11, 10, 11, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10};
+
+const int _cTableCredDeu[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 81, -1, -1, -1, 90, 91, 111,
+ 84, 85, 99, 93, 95, -1, 100, 92, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 89, 94,
+ -1, 97, -1, 79, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 96, 98, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 77,
+ -1, 86, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 87, -1, -1,
+ -1, -1, -1, -1, -1, -1, 55, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 67, -1, -1, -1, -1, -1,
+ 71, -1, -1, 234, 52, 53, -1, -1, 55, -1,
+ -1, 74, 56, 57, -1, -1 , 60, 61, -1, -1,
+ -1, 73, 64, 65, -1, -1, 67, -1, -1, 68,
+ 69, -1, 71, -1, -1, -1};
+
+const int _lTableCredDeu[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 11, 10, 10, 10, 10, 10, 10, 5,
+ 10, 10, 10, 10, 5, 10, 5, 10, 12, 8,
+ 10, 11, 12, 11, 12, 10, 11, 10, 5, 5,
+ 10, 10, 10, 10, 10, 19, 15, 14, 13, 14,
+ 13, 16, 15, 5, 8, 15, 13, 17, 15, 14,
+ 12, 14, 14, 15, 11, 12, 12, 16, 12, 13,
+ 14, 10, 10, 10, 9, 10, 10, 11, 9, 9,
+ 10, 9, 8, 9, 10, 5, 6, 12, 6, 14,
+ 10, 11, 11, 9, 9, 9, 6, 9, 10, 14,
+ 9, 10, 9, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 19, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 11, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10};
+
+const int _cTableObjIta[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 42, 51, -1, 53, 54, 55, 50,
+ 47, 48, 57, 41, 36, 40, 38, 46, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 39, 37,
+ 58, 49, 59, 44, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 56, -1, -1, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, 93, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, 93,
+ -1, -1, -1, 88, -1, 85, -1, -1, -1, 67,
+ -1, 60, -1, -1, 66, 92, -1, -1, -1, 88,
+ -1, -1, -1, -1, -1, 85, -1, 61, -1, -1,
+ -1, 92, -1, 69, -1, -1, 70, 71, 86, -1,
+ 72, -1, 87, 73, 75, -1, -1, 76, -1, 89,
+ 78, 90, -1, -1, 79, -1, -1, 81, -1, -1,
+ 82, -1, -1, -1, -1, -1, -1, -1, 70, -1,
+ 86, 63, -1, -1, 87, -1, -1, -1, -1, -1,
+ -1, 89, -1, 90, -1, -1, 79, -1, 62, -1,
+ -1, -1, 82, -1, -1, -1};
+
+const int _lTableObjIta[] = {
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 11, 26, 26, 26, 26, 26, 26, 8,
+ 26, 26, 26, 26, 26, 12, 8, 26, 20, 20,
+ 15, 20, 20, 20, 20, 20, 20, 20, 26, 26,
+ 26, 26, 26, 26, 26, 17, 17, 19, 17, 15,
+ 17, 19, 17, 16, 26, 17, 14, 19, 17, 19,
+ 17, 19, 14, 13, 15, 15, 13, 19, 15, 13,
+ 20, 26, 26, 26, 26, 26, 26, 17, 17, 19,
+ 17, 15, 17, 19, 17, 16, 26, 17, 14, 19,
+ 17, 19, 17, 19, 14, 13, 15, 15, 13, 19,
+ 15, 13, 20, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 15, 26, 26, 21, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 15, 26, 26, 21,
+ 26, 26, 26, 19, 26, 20, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 21, 26, 26, 26, 19,
+ 26, 26, 26, 26, 26, 20, 26, 26, 26, 26,
+ 26, 21, 26, 26, 26, 26, 17, 26, 22, 26,
+ 26, 26, 17, 26, 26, 26, 26, 26, 26, 17,
+ 26, 22, 26, 26, 19, 26, 26, 26, 26, 26,
+ 15, 26, 26, 26, 26, 26, 26, 26, 17, 26,
+ 22, 26, 26, 26, 17, 26, 26, 26, 26, 26,
+ 26, 17, 26, 22, 26, 26, 19, 26, 26, 26,
+ 26, 26, 15, 26, 26, 26};
+
+const int _cTableObjPol[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 42, 51, -1, 53, 54, 55, 50,
+ 47, 48, 57, 41, 36, 40, 38, 46, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 39, 37,
+ 58, 49, 59, 44, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 56, -1, -1, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, 93, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, 93,
+ -1, -1, -1, 88, -1, 85, -1, -1, -1, 67,
+ -1, 60, -1, -1, 66, 92, -1, -1, -1, 88,
+ -1, -1, -1, -1, -1, 85, -1, 61, -1, -1,
+ -1, 92, -1, 69, -1, -1, 70, 71, 86, -1,
+ 72, -1, 87, 73, 75, -1, -1, 76, -1, 89,
+ 78, 90, -1, -1, 79, -1, -1, 81, -1, -1,
+ 82, -1, -1, -1, -1, -1, -1, -1, 70, -1,
+ 86, 63, -1, -1, 87, -1, -1, -1, -1, -1,
+ -1, 89, -1, 90, -1, -1, 79, -1, 62, -1,
+ -1, -1, 82, -1, -1, -1};
+
+const int _lTableObjPol[] = {
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 11, 26, 26, 26, 26, 26, 26, 8,
+ 26, 26, 26, 26, 26, 12, 8, 26, 20, 20,
+ 15, 20, 20, 20, 20, 20, 20, 20, 26, 26,
+ 26, 26, 26, 26, 26, 17, 17, 19, 17, 15,
+ 17, 19, 17, 16, 26, 17, 14, 19, 17, 19,
+ 17, 19, 14, 13, 15, 15, 13, 19, 15, 13,
+ 20, 26, 26, 26, 26, 26, 26, 17, 17, 19,
+ 17, 15, 17, 19, 17, 16, 26, 17, 14, 19,
+ 17, 19, 17, 19, 14, 13, 15, 15, 13, 19,
+ 15, 13, 20, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 15, 26, 26, 21, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 15, 26, 26, 21,
+ 26, 26, 26, 19, 26, 20, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 21, 26, 26, 26, 19,
+ 26, 26, 26, 26, 26, 20, 26, 26, 26, 26,
+ 26, 21, 26, 26, 26, 26, 17, 26, 22, 26,
+ 26, 26, 17, 26, 26, 26, 26, 26, 26, 17,
+ 26, 22, 26, 26, 19, 26, 26, 26, 26, 26,
+ 15, 26, 26, 26, 26, 26, 26, 26, 17, 26,
+ 22, 26, 26, 26, 17, 26, 26, 26, 26, 26,
+ 26, 17, 26, 22, 26, 26, 19, 26, 26, 26,
+ 26, 26, 15, 26, 26, 26};
+
+const int _cTableObjRus[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 42, 51, -1, 53, 54, 55, 50,
+ 47, 48, 57, 41, 36, 40, 38, 46, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 39, 37,
+ 58, 49, 59, 44, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 56, -1, -1, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, 93, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, 93,
+ -1, -1, -1, 88, -1, 85, -1, -1, 100, 67,
+ -1, 60, -1, -1, 66, 92, -1, -1, -1, 88,
+ -1, -1, -1, -1, 100, 85, -1, 61, -1, -1,
+ -1, 92, 94, 95, 96, 97, 98, 99, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 122, 123,
+ 121, 124, 125, 126, 94, 95, 96, 97, 98, 99,
+ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
+ 122, 123, 121, 124, 125, 126};
+
+const int _lTableObjRus[] = {
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 11, 26, 26, 26, 26, 26, 26, 8,
+ 26, 26, 26, 26, 26, 12, 8, 26, 20, 20,
+ 15, 20, 20, 20, 20, 20, 20, 20, 26, 26,
+ 26, 26, 26, 26, 26, 17, 17, 19, 17, 15,
+ 17, 19, 17, 16, 26, 17, 14, 19, 17, 19,
+ 17, 19, 14, 13, 15, 15, 13, 19, 15, 13,
+ 20, 26, 26, 26, 26, 26, 26, 17, 17, 19,
+ 17, 15, 17, 19, 17, 16, 26, 17, 14, 19,
+ 17, 19, 17, 19, 14, 13, 15, 15, 13, 19,
+ 15, 13, 20, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 15, 26, 26, 21, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 15, 26, 26, 21,
+ 26, 26, 26, 19, 26, 20, 26, 26, 18, 26,
+ 26, 26, 26, 26, 26, 21, 26, 26, 26, 19,
+ 26, 26, 26, 26, 18, 20, 26, 26, 26, 26,
+ 26, 21, 18, 18, 18, 17, 16, 18, 20, 18,
+ 18, 18, 18, 16, 18, 15, 22, 15, 18, 22,
+ 19, 16, 21, 20, 16, 16, 19, 22, 19, 19,
+ 18, 15, 18, 18, 18, 18, 18, 17, 16, 18,
+ 20, 18, 18, 18, 18, 16, 18, 15, 22, 15,
+ 18, 22, 19, 16, 21, 20, 16, 16, 19, 22,
+ 19, 19, 18, 15, 18, 18};
+
+const int _cTableObjCze[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 42, 51, -1, 53, 54, 55, 50,
+ 47, 48, 57, 41, 36, 40, 38, 46, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 39, 37,
+ 58, 49, 59, 44, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 56, -1, -1, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 128, -1,
+ 91, 137, 131, 93, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 128, -1, 91, 137, 131, 93,
+ -1, -1, -1, 88, -1, 85, -1, -1, -1, 67,
+ -1, 60, -1, -1, 66, 92, -1, -1, -1, 88,
+ -1, -1, -1, -1, -1, 85, -1, 61, -1, -1,
+ -1, 92, -1, 133, -1, -1, 70, 71, 86, -1,
+ 129, 135, 87, 73, 127, 134, -1, 136, -1, 89,
+ 138, 139, -1, -1, 79, -1, 130, 140, 141, -1,
+ 82, 132, -1, -1, -1, 133, -1, -1, 70, -1,
+ 86, 63, 129, 135, 87, -1, 127, 134, -1, 136,
+ -1, 89, 138, 139, -1, -1, 79, -1, 130, 140,
+ 141, -1, 82, 132, -1, -1};
+
+const int _lTableObjCze[] = {
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 11, 26, 26, 26, 26, 26, 26, 8,
+ 26, 26, 26, 26, 26, 12, 8, 26, 20, 20,
+ 15, 20, 20, 20, 20, 20, 20, 20, 26, 26,
+ 26, 26, 26, 26, 26, 17, 17, 19, 17, 15,
+ 17, 19, 17, 16, 26, 17, 14, 19, 17, 19,
+ 17, 19, 14, 13, 15, 15, 13, 19, 15, 13,
+ 20, 26, 26, 26, 26, 26, 26, 17, 17, 19,
+ 17, 15, 17, 19, 17, 16, 26, 17, 14, 19,
+ 17, 19, 17, 19, 14, 13, 15, 15, 13, 19,
+ 15, 13, 20, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 15, 26,
+ 15, 24, 21, 21, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 15, 26, 15, 24, 21, 21,
+ 26, 26, 26, 19, 26, 20, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 21, 26, 26, 26, 19,
+ 26, 26, 26, 26, 26, 20, 26, 26, 26, 26,
+ 26, 21, 26, 18, 26, 26, 17, 26, 22, 26,
+ 22, 17, 17, 26, 17, 19, 26, 23, 26, 17,
+ 17, 22, 26, 26, 19, 26, 18, 16, 16, 26,
+ 15, 16, 26, 26, 26, 18, 26, 26, 17, 26,
+ 22, 26, 22, 17, 17, 26, 17, 19, 26, 23,
+ 26, 17, 17, 22, 26, 26, 19, 26, 18, 16,
+ 16, 26, 15, 16, 26, 26};
+
+const int _cTableObjFra[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 42, 51, -1, 53, 54, 55, 50,
+ 47, 48, 57, 41, 36, 40, 38, 46, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 39, 37,
+ 58, 49, 59, 44, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 56, -1, -1, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, 93, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, 93,
+ -1, -1, -1, 88, -1, 85, -1, -1, -1, 67,
+ -1, 60, -1, -1, 66, 92, -1, -1, -1, 88,
+ -1, -1, -1, -1, -1, 85, -1, 61, -1, -1,
+ -1, 92, -1, 69, -1, -1, 70, 71, 86, -1,
+ 72, -1, 87, 73, 75, -1, -1, 76, -1, 89,
+ 78, 90, -1, -1, 79, -1, -1, 81, -1, -1,
+ 82, -1, -1, -1, 0, 0, 0, -1, 70, -1,
+ 86, 63, 4, 4, 87, -1, 8, 8, 8, -1,
+ -1, 89, 14, 14, 14, -1, 14, -1, 62, 20,
+ -1, 20, 82, -1, -1, -1};
+
+const int _lTableObjFra[] = {
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 11, 26, 26, 26, 26, 26, 26, 8,
+ 26, 26, 26, 26, 26, 12, 8, 26, 20, 20,
+ 15, 20, 20, 20, 20, 20, 20, 20, 26, 26,
+ 26, 26, 26, 26, 26, 17, 17, 19, 17, 15,
+ 17, 19, 17, 16, 26, 17, 14, 19, 17, 19,
+ 17, 19, 14, 13, 15, 15, 13, 19, 15, 13,
+ 20, 26, 26, 26, 26, 26, 26, 17, 17, 19,
+ 17, 15, 17, 19, 17, 16, 26, 17, 14, 19,
+ 17, 19, 17, 19, 14, 13, 15, 15, 13, 19,
+ 15, 13, 20, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 15, 26, 26, 21, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 15, 26, 26, 21,
+ 26, 26, 26, 19, 26, 20, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 21, 26, 26, 26, 19,
+ 26, 26, 26, 26, 26, 20, 26, 26, 26, 26,
+ 26, 21, 26, 26, 26, 26, 17, 26, 22, 26,
+ 26, 26, 17, 26, 26, 26, 26, 26, 26, 17,
+ 26, 22, 26, 26, 19, 26, 26, 26, 26, 26,
+ 15, 26, 26, 26, 17, 17, 17, 26, 17, 26,
+ 22, 26, 15, 15, 17, 26, 16, 16, 16, 26,
+ 26, 17, 19, 19, 19, 26, 19, 26, 26, 15,
+ 26, 15, 15, 26, 26, 26};
+
+const int _cTableObjDeu[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 42, 51, -1, 53, 54, 55, 50,
+ 47, 48, 57, 41, 36, 40, 38, 46, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 39, 37,
+ 58, 49, 59, 44, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, 56, -1, -1, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, 93, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, 93,
+ -1, -1, -1, 88, -1, 85, -1, -1, -1, 67,
+ -1, 60, -1, -1, 66, 92, -1, -1, -1, 88,
+ -1, -1, -1, -1, -1, 85, -1, 61, -1, -1,
+ -1, 92, -1, 69, -1, -1, 70, 71, 86, -1,
+ 72, -1, 87, 73, 75, -1, -1, 76, -1, 89,
+ 78, 90, -1, -1, 79, -1, -1, 81, -1, -1,
+ 82, -1, -1, 142, -1, -1, -1, -1, 70, -1,
+ 86, 63, -1, -1, 87, -1, -1, -1, -1, -1,
+ -1, 89, -1, 90, -1, -1, 79, -1, 62, -1,
+ -1, -1, 82, -1, -1, -1};
+
+const int _lTableObjDeu[] = {
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 11, 26, 26, 26, 26, 26, 26, 8,
+ 26, 26, 26, 26, 26, 12, 8, 26, 20, 20,
+ 15, 20, 20, 20, 20, 20, 20, 20, 26, 26,
+ 26, 26, 26, 26, 26, 17, 17, 19, 17, 15,
+ 17, 19, 17, 16, 26, 17, 14, 19, 17, 19,
+ 17, 19, 14, 13, 15, 15, 13, 19, 15, 13,
+ 20, 26, 26, 26, 26, 26, 26, 17, 17, 19,
+ 17, 15, 17, 19, 17, 16, 26, 17, 14, 19,
+ 17, 19, 17, 19, 14, 13, 15, 15, 13, 19,
+ 15, 13, 20, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 15, 26, 26, 21, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 15, 26, 26, 21,
+ 26, 26, 26, 19, 26, 20, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 21, 26, 26, 26, 19,
+ 26, 26, 26, 26, 26, 20, 26, 26, 26, 26,
+ 26, 21, 26, 26, 26, 26, 17, 26, 22, 26,
+ 26, 26, 17, 26, 26, 26, 26, 26, 26, 17,
+ 26, 22, 26, 26, 19, 26, 26, 26, 26, 26,
+ 15, 26, 26, 24, 26, 26, 26, 26, 17, 26,
+ 22, 26, 26, 26, 17, 26, 26, 26, 26, 26,
+ 26, 17, 26, 22, 26, 26, 19, 26, 26, 26,
+ 26, 26, 15, 26, 26, 26};
+
+#endif
diff --git a/devtools/credits.pl b/devtools/credits.pl
index 7ce17a9df6..e04e35d3be 100755
--- a/devtools/credits.pl
+++ b/devtools/credits.pl
@@ -48,7 +48,7 @@ if ($mode eq "") {
$Text::Wrap::unexpand = 0;
if ($mode eq "TEXT") {
$Text::Wrap::columns = 78;
- $max_name_width = 21; # The maximal width of a name.
+ $max_name_width = 23; # The maximal width of a name.
} elsif ($mode eq "CPP") {
$Text::Wrap::columns = 48; # Approx.
}
@@ -359,6 +359,9 @@ sub add_person {
my $min_name_width = length $desc > 0 ? $max_name_width : 0;
$name = $nick if $name eq "";
$name = html_entities_to_ascii($name);
+ if (length $name > $max_name_width) {
+ print STDERR "Warning: max_name_width is too small (" . $max_name_width . " < " . (length $name) . " for \"" . $name. "\")\n";
+ }
$desc = html_entities_to_ascii($desc);
$tab = " " x ($section_level * 2 + 1);
@@ -613,6 +616,10 @@ begin_credits("Credits");
add_person("", "peres", "");
end_section();
+ begin_section("Pegasus");
+ add_person("Matthew Hoops", "clone2727", "");
+ end_section();
+
begin_section("Queen");
add_person("David Eriksson", "twogood", "(retired)");
add_person("Gregory Montoir", "cyx", "(retired)");
@@ -680,6 +687,12 @@ begin_credits("Credits");
add_person("Joost Peters", "joostp", "");
end_section();
+ begin_section("Tony");
+ add_person("Arnaud Boutonn&eacute;", "Strangerke", "");
+ add_person("Paul Gilbert", "dreammaster", "");
+ add_person("Alyssa Milburn", "fuzzie", "");
+ end_section();
+
begin_section("Toon");
add_person("Sylvain Dupont", "SylvainTV", "");
end_section();
@@ -697,6 +710,10 @@ begin_credits("Credits");
add_person("Gregory Montoir", "cyx", "(retired)");
end_section();
+ begin_section("Wintermute");
+ add_person("Einar Johan T. S&oslash;m&aring;en", "somaen", "");
+ end_section();
+
end_section();
@@ -940,7 +957,7 @@ begin_credits("Credits");
end_section();
begin_section("German");
add_person("Simon Sawatzki", "SimSaw", "");
- add_person("Lothar Serra Mari", "Lothar93", "");
+ add_person("Lothar Serra Mari", "Lothar93", "(retired)");
end_section();
begin_section("Hungarian");
add_person("Alex Bevilacqua", "", "");
@@ -1067,7 +1084,7 @@ begin_credits("Credits");
# HACK!
- $max_name_width = 16;
+ $max_name_width = 17;
begin_section("Special thanks to");
begin_persons();
@@ -1080,6 +1097,7 @@ begin_credits("Credits");
add_person("Ivan Dubrov", "", "For contributing the initial version of the Gobliiins engine");
add_person("Henrik Engqvist", "qvist", "For generously providing hosting for our buildbot, SVN repository, planet and doxygen sites as well as tons of HD space");
add_person("DOSBox Team", "", "For their awesome OPL2 and OPL3 emulator");
+ add_person("Yusuke Kamiyamane", "", "For contributing some GUI icons ");
add_person("Till Kresslein", "Krest", "For design of modern ScummVM GUI");
add_person("", "Jezar", "For his freeverb filter implementation");
add_person("Jim Leiterman", "", "Various info on his FM-TOWNS/Marty SCUMM ports");
@@ -1090,8 +1108,6 @@ begin_credits("Credits");
add_person("James Woodcock", "", "Soundtrack enhancements");
end_persons();
- add_paragraph("Some icons by Yusuke Kamiyamane");
-
add_paragraph(
"Tony Warriner and everyone at Revolution Software Ltd. for sharing ".
"with us the source of some of their brilliant games, allowing us to ".
@@ -1141,7 +1157,15 @@ begin_credits("Credits");
add_paragraph(
"Janusz Wi&#347;niewski and Miroslaw Liminowicz from Laboratorium Komputerowe Avalon ".
- "for providing full source code for So&#322;tys and letting us to redistribute the game.");
+ "for providing full source code for So&#322;tys and letting us redistribute the game.");
+
+ add_paragraph(
+ "Jan Nedoma for providing the sources to the Wintermute-engine, and for his ".
+ "support while porting the engine to ScummVM.");
+
+ add_paragraph(
+ "Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for ".
+ "providing the source code of The Journeyman Project: Pegasus Prime.");
end_section();
diff --git a/devtools/module.mk b/devtools/module.mk
index 95eca50d18..1d682cdf05 100644
--- a/devtools/module.mk
+++ b/devtools/module.mk
@@ -56,7 +56,7 @@ credits:
$(srcdir)/devtools/credits.pl --text > $(srcdir)/AUTHORS
# $(srcdir)/devtools/credits.pl --rtf > $(srcdir)/Credits.rtf
$(srcdir)/devtools/credits.pl --cpp > $(srcdir)/gui/credits.h
- $(srcdir)/devtools/credits.pl --xml-website > $(srcdir)/../../web/trunk/data/credits.xml
+ $(srcdir)/devtools/credits.pl --xml-website > $(srcdir)/../scummvm-web/data/credits.xml
# $(srcdir)/devtools/credits.pl --xml-docbook > $(srcdir)/../../docs/trunk/docbook/credits.xml
md5scumm: devtools/md5table$(EXEEXT)
diff --git a/dists/engine-data/README b/dists/engine-data/README
index ce74d590d9..c9c4bd4817 100644
--- a/dists/engine-data/README
+++ b/dists/engine-data/README
@@ -1,6 +1,9 @@
engine-data README
-------------------------------------------------------------------------------
+drascula.dat
+TODO
+
hugo.dat:
This file contains all the hardcoded logic, strings and fonts used by Hugo
engine. Those information were stored in the original executables.
@@ -10,6 +13,9 @@ The 'kyra.dat' file is created by extracting hardcoded data, like the
roomtable, inventory names, various strings, tables for shapes and sequence
scripts, from.
+lure.dat
+TODO
+
queen.tbl:
'queen.tbl' contains a list of filenames, filesizes and offsets for the
individual files saved in QUEEN.1. This data was originally included in the
@@ -20,5 +26,11 @@ mp3/ogg/flac encoded need the datafile.
sky.cpt:
TODO
+teenagent.dat
+TODO
+
+tony.dat:
+This file contains the font table used by the different versions of the game.
+
toon.dat:
'toon.dat' contains all the strings hardcoded in the original executables.
diff --git a/dists/engine-data/tony.dat b/dists/engine-data/tony.dat
new file mode 100644
index 0000000000..5ea53c6752
--- /dev/null
+++ b/dists/engine-data/tony.dat
Binary files differ
diff --git a/dists/scummvm.rc b/dists/scummvm.rc
index 4a67100f9f..8527c668c8 100644
--- a/dists/scummvm.rc
+++ b/dists/scummvm.rc
@@ -44,6 +44,9 @@ teenagent.dat FILE "dists/engine-data/teenagent.dat"
#if ENABLE_TOON == STATIC_PLUGIN
toon.dat FILE "dists/engine-data/toon.dat"
#endif
+#if ENABLE_TONY == STATIC_PLUGIN
+tony.dat FILE "dists/engine-data/tony.dat"
+#endif
#if ENABLE_AGI == STATIC_PLUGIN
pred.dic FILE "dists/pred.dic"
#endif
diff --git a/dists/win32/ScummVM.iss b/dists/win32/ScummVM.iss
index c07b0ae64f..adea759110 100644
--- a/dists/win32/ScummVM.iss
+++ b/dists/win32/ScummVM.iss
@@ -108,18 +108,6 @@ Source: doc/se/LasMig.txt; DestDir: {app}; Flags: ignoreversion isreadme; Langua
Source: README-SDL.txt; DestDir: {app}; Flags: ignoreversion
Source: scummvm.exe; DestDir: {app}; Flags: ignoreversion
Source: SDL.dll; DestDir: {app}
-Source: scummclassic.zip; DestDir: {app}; Flags: ignoreversion
-Source: scummmodern.zip; DestDir: {app}; Flags: ignoreversion
-Source: drascula.dat; DestDir: {app}; Flags: ignoreversion
-Source: hugo.dat; DestDir: {app}; Flags: ignoreversion
-Source: kyra.dat; DestDir: {app}; Flags: ignoreversion
-Source: lure.dat; DestDir: {app}; Flags: ignoreversion
-Source: pred.dic; DestDir: {app}; Flags: ignoreversion
-Source: queen.tbl; DestDir: {app}; Flags: ignoreversion
-Source: sky.cpt; DestDir: {app}; Flags: ignoreversion
-Source: teenagent.dat; DestDir: {app}; Flags: ignoreversion
-Source: toon.dat; DestDir: {app}; Flags: ignoreversion
-Source: translations.dat; DestDir: {app}; Flags: ignoreversion
;Mirgration script for saved games in Windows NT4 onwards
Source: migration.bat; DestDir: {app}; Flags: ignoreversion; MinVersion: 0, 1
Source: migration.txt; DestDir: {app}; Flags: ignoreversion; MinVersion: 0, 1
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index 8c19d03691..3eec33abe5 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -194,7 +194,7 @@ protected:
/**
* A map containing all the extra game GUI options the engine supports.
- */
+ */
const ADExtraGuiOptionsMap * const _extraGuiOptions;
/**
@@ -212,7 +212,7 @@ protected:
*
* Used to override gameid.
* This is a recommended setting to prevent global gameid pollution.
- * With this option set, the gameid effectively turns into engineid.
+ * With this option set, the gameid effectively turns into engineid.
*
* FIXME: This field actually removes a feature (gameid) in order to
* address a more generic problem. We should find a better way to
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index 45c00a76ac..98ffca22ed 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -219,7 +219,7 @@ void AgiEngine::processEvents() {
case Common::KEYCODE_F6:
key = 0x4000;
break;
- case Common::KEYCODE_F7:
+ case Common::KEYCODE_F7:
key = 0x4100;
break;
case Common::KEYCODE_F8:
diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h
index ab0e9a1fe4..9d67b15adb 100644
--- a/engines/agi/detection_tables.h
+++ b/engines/agi/detection_tables.h
@@ -706,10 +706,10 @@ static const AGIGameDescription gameDescriptions[] = {
FANMADE("Go West, Young Hippie", "ff31484ea465441cb5f3a0f8e956b716"),
FANMADE("Good Man (demo v3.41)", "3facd8a8f856b7b6e0f6c3200274d88c"),
- GAME_LVFPNF("agi-fanmade", "Groza (russian) [AGDS sample]", "logdir", "421da3a18004122a966d64ab6bd86d2e", -1,
+ GAME_LVFPNF("agi-fanmade", "Groza (russian) [AGDS sample]", "logdir", "421da3a18004122a966d64ab6bd86d2e", -1,
Common::RU_RUS, 0x2440, GF_AGDS, GID_FANMADE, Common::kPlatformPC,GType_V2),
- GAME_LVFPNF("agi-fanmade", "Get Outta Space Quest", "logdir", "aaea5b4a348acb669d13b0e6f22d4dc9", -1,
+ GAME_LVFPNF("agi-fanmade", "Get Outta Space Quest", "logdir", "aaea5b4a348acb669d13b0e6f22d4dc9", -1,
Common::EN_ANY, 0x2440, GF_FANMADE, GID_GETOUTTASQ, Common::kPlatformPC,GType_V2),
FANMADE_F("Half-Death - Terror At White-Mesa", "b62c05d0ace878261392073f57ae788c", GF_AGIMOUSE),
diff --git a/engines/agi/loader_v1.cpp b/engines/agi/loader_v1.cpp
index c6a3e66705..189c98ee98 100644
--- a/engines/agi/loader_v1.cpp
+++ b/engines/agi/loader_v1.cpp
@@ -64,7 +64,7 @@ int AgiLoader_v1::detectGame() {
int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) {
Common::File fp;
-
+
if (!fp.open(_filenameDisk0))
return errBadFileOpen;
@@ -73,13 +73,13 @@ int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) {
agid[i].volume = 0xFF;
agid[i].offset = _EMPTY;
}
-
+
fp.seek(offset, SEEK_SET);
for (int i = 0; i <= max; i++) {
int b0 = fp.readByte();
int b1 = fp.readByte();
int b2 = fp.readByte();
-
+
if (b0 == 0xFF && b1 == 0xFF && b2 == 0xFF) {
agid[i].volume = 0xFF;
agid[i].offset = _EMPTY;
@@ -98,7 +98,7 @@ int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) {
int AgiLoader_v1::loadDir_BC(AgiDir *agid, int offset, int max) {
Common::File fp;
-
+
if (!fp.open(_filenameDisk0))
return errBadFileOpen;
@@ -107,13 +107,13 @@ int AgiLoader_v1::loadDir_BC(AgiDir *agid, int offset, int max) {
agid[i].volume = 0xFF;
agid[i].offset = _EMPTY;
}
-
+
fp.seek(offset, SEEK_SET);
for (int i = 0; i <= max; i++) {
int b0 = fp.readByte();
int b1 = fp.readByte();
int b2 = fp.readByte();
-
+
if (b0 == 0xFF && b1 == 0xFF && b2 == 0xFF) {
agid[i].volume = 0xFF;
agid[i].offset = _EMPTY;
@@ -171,7 +171,7 @@ uint8 *AgiLoader_v1::loadVolRes(struct AgiDir *agid) {
if (offset == _EMPTY)
return NULL;
-
+
if (offset > IMAGE_SIZE) {
fp.open(_filenameDisk1);
offset -= IMAGE_SIZE;
@@ -191,7 +191,7 @@ uint8 *AgiLoader_v1::loadVolRes(struct AgiDir *agid) {
agid->len = fp.readUint16LE();
data = (uint8 *)calloc(1, agid->len + 32);
fp.read(data, agid->len);
-
+
fp.close();
return data;
diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp
index cac1701596..d23a5a2e27 100644
--- a/engines/agi/menu.cpp
+++ b/engines/agi/menu.cpp
@@ -289,7 +289,7 @@ bool Menu::keyhandler(int key) {
_vm->_game.clockEnabled = false;
drawMenuBar();
}
-
+
// Mouse handling
if (_vm->_mouse.button) {
int hmenu, vmenu;
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index 7e04328a67..5334407eb8 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -1146,7 +1146,7 @@ void cmdFollowEgo(AgiGame *state, uint8 *p) {
vt.parm1 = p1 > vt.stepSize ? p1 : vt.stepSize;
vt.parm2 = p2;
vt.parm3 = 0xff;
-
+
if (getVersion() < 0x2000) {
_v[p2] = 0;
vt.flags |= fUpdate | fAnimated;
@@ -1270,7 +1270,7 @@ void cmdVersion(AgiGame *state, uint8 *p) {
// no Sierra as it wraps textbox
Common::String verMsg = TITLE " v%s";
-
+
int ver = getVersion();
int maj = (ver >> 12) & 0xf;
int min = ver & 0xfff;
@@ -1839,7 +1839,7 @@ int AgiEngine::runLogic(int n) {
// ip = 2;
// warning("running logic %d\n", n);
// }
-
+
if (_game.exitAllLogics)
break;
}
diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp
index a44c68e0fc..4d5e6fffe1 100644
--- a/engines/agi/op_test.cpp
+++ b/engines/agi/op_test.cpp
@@ -403,7 +403,7 @@ int AgiEngine::testIfCode(int lognum) {
case 0xFF:
endTest = true;
continue;
-
+
default:
// Evaluate the command and skip the rest of the instruction
_agiCondCommands[op](state, p);
diff --git a/engines/agi/opcodes.cpp b/engines/agi/opcodes.cpp
index 29fb860635..7a427bd94f 100644
--- a/engines/agi/opcodes.cpp
+++ b/engines/agi/opcodes.cpp
@@ -130,7 +130,7 @@ AgiInstruction insV1[] = {
{ "...", "", &cmdUnknown }, // 4E # show.obj
{ "load.logics", "n", &cmdLoadLogic }, // 4F # load.global.logics
{ "display", "nnns", &cmdDisplay }, // 50 TODO: 4 vs 3 args
- { "prevent.input???", "", &cmdUnknown }, // 51
+ { "prevent.input???", "", &cmdUnknown }, // 51
{ "...", "", &cmdUnknown }, // 52 # nop
{ "...", "n", &cmdUnknown }, // 53 # text.screen
{ "...", "", &cmdUnknown }, // 54 ???
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index 3e63da756d..d451a799a0 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -821,7 +821,7 @@ int AgiEngine::scummVMSaveLoadDialog(bool isSave) {
if (slot < 0)
return true;
-
+
if (isSave)
return doSave(slot, desc);
else
diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp
index d21baa450f..5bffca5765 100644
--- a/engines/agi/sound_pcjr.cpp
+++ b/engines/agi/sound_pcjr.cpp
@@ -234,7 +234,7 @@ int SoundGenPCJr::getNextNote(int ch)
// if tone isn't touched.. it should be inited so it just plays silence
// return 0 if it's passing more data
// return -1 if it's passing nothing (end of data)
-int SoundGenPCJr::getNextNote_v2(int ch) {
+int SoundGenPCJr::getNextNote_v2(int ch) {
ToneChan *tpcm;
SndGenChan *chan;
const byte *data;
@@ -308,7 +308,7 @@ int SoundGenPCJr::getNextNote_v1(int ch) {
_channel[ch].attenuationCopy = 0x0F;
return -1;
}
-
+
// In the V1 player the default duration for a row is 3 ticks
if (duration > 0) {
duration--;
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index 1886a74ab1..4877be2647 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -64,7 +64,7 @@ void AgiEngine::printText2(int l, const char *msg, int foff, int xoff, int yoff,
// Note: there were extra checks for *m being a cursor character
// here (1, 2 or 3), which have been removed, as the cursor
- // character is no longer printed via this function.
+ // character is no longer printed via this function.
if (*m >= 0x20) {
int ypos = (y1 * CHAR_LINES) + yoff;
@@ -73,7 +73,7 @@ void AgiEngine::printText2(int l, const char *msg, int foff, int xoff, int yoff,
if (xpos >= GFX_WIDTH)
continue;
-
+
_gfx->putTextCharacter(l, xpos, ypos, *m, fg, bg, checkerboard);
if (x1 > maxx)
diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp
index 4400112247..9c5b3d349a 100644
--- a/engines/agi/words.cpp
+++ b/engines/agi/words.cpp
@@ -41,7 +41,7 @@ int AgiEngine::loadWords_v1(Common::File &f) {
int k;
debug(0, "Loading dictionary");
-
+
// Loop through alphabet, as words in the dictionary file are sorted by
// first character
f.seek(f.pos() + 26 * 2, SEEK_SET);
@@ -131,7 +131,7 @@ int AgiEngine::findWord(const char *word, int *flen) {
*flen = 0;
Common::Array<AgiWord *> &a = _game.words[c];
-
+
for (int i = 0; i < (int)a.size(); i++) {
int wlen = strlen(a[i]->word);
// Keep looking till we find the word itself, or the whole phrase.
diff --git a/engines/agos/midiparser_s1d.cpp b/engines/agos/midiparser_s1d.cpp
index 9ca87436fc..bef7199a98 100644
--- a/engines/agos/midiparser_s1d.cpp
+++ b/engines/agos/midiparser_s1d.cpp
@@ -35,7 +35,7 @@ namespace AGOS {
class MidiParser_S1D : public MidiParser {
private:
byte *_data;
- bool _no_delta;
+ bool _noDelta;
struct Loop {
uint16 timer;
@@ -49,7 +49,7 @@ protected:
void resetTracking();
public:
- MidiParser_S1D() : _data(0), _no_delta(false) {}
+ MidiParser_S1D() : _data(0), _noDelta(false) {}
bool loadMusic(byte *data, uint32 size);
};
@@ -75,14 +75,14 @@ void MidiParser_S1D::chainEvent(EventInfo &info) {
}
void MidiParser_S1D::parseNextEvent(EventInfo &info) {
- info.start = _position._play_pos;
+ info.start = _position._playPos;
info.length = 0;
- info.delta = _no_delta ? 0 : readVLQ2(_position._play_pos);
- _no_delta = false;
+ info.delta = _noDelta ? 0 : readVLQ2(_position._playPos);
+ _noDelta = false;
- info.event = *_position._play_pos++;
+ info.event = *_position._playPos++;
if (!(info.event & 0x80)) {
- _no_delta = true;
+ _noDelta = true;
info.event |= 0x80;
}
@@ -94,34 +94,43 @@ void MidiParser_S1D::parseNextEvent(EventInfo &info) {
} else {
switch (info.command()) {
case 0x8: // note off
- info.basic.param1 = *_position._play_pos++;
+ info.basic.param1 = *_position._playPos++;
info.basic.param2 = 0;
break;
case 0x9: // note on
- info.basic.param1 = *_position._play_pos++;
- info.basic.param2 = *_position._play_pos++;
+ info.basic.param1 = *_position._playPos++;
+ info.basic.param2 = *_position._playPos++;
+ // Rewrite note on events with velocity 0 as note off events.
+ // This is the actual meaning of this, but theoretically this
+ // should not need to be rewritten, since all MIDI devices should
+ // interpret it like that. On the other hand all our MidiParser
+ // implementations do it and there seems to be code in MidiParser
+ // which relies on this for tracking active notes.
+ if (info.basic.param2 == 0) {
+ info.event = info.channel() | 0x80;
+ }
break;
case 0xA: { // loop control
// In case the stop mode(?) is set to 0x80 this will stop the
// track over here.
- const int16 loopIterations = int8(*_position._play_pos++);
+ const int16 loopIterations = int8(*_position._playPos++);
if (!loopIterations) {
- _loops[info.channel()].start = _position._play_pos;
+ _loops[info.channel()].start = _position._playPos;
} else {
if (!_loops[info.channel()].timer) {
if (_loops[info.channel()].start) {
_loops[info.channel()].timer = uint16(loopIterations);
- _loops[info.channel()].end = _position._play_pos;
+ _loops[info.channel()].end = _position._playPos;
// Go to the start of the loop
- _position._play_pos = _loops[info.channel()].start;
+ _position._playPos = _loops[info.channel()].start;
}
} else {
if (_loops[info.channel()].timer)
- _position._play_pos = _loops[info.channel()].start;
+ _position._playPos = _loops[info.channel()].start;
--_loops[info.channel()].timer;
}
}
@@ -141,13 +150,13 @@ void MidiParser_S1D::parseNextEvent(EventInfo &info) {
break;
case 0xC: // program change
- info.basic.param1 = *_position._play_pos++;
+ info.basic.param1 = *_position._playPos++;
info.basic.param2 = 0;
break;
case 0xD: // jump to loop end
if (_loops[info.channel()].end)
- _position._play_pos = _loops[info.channel()].end;
+ _position._playPos = _loops[info.channel()].end;
// We need to read the next midi event here. Since we can not
// safely pass this event to the MIDI event processing.
@@ -178,7 +187,7 @@ bool MidiParser_S1D::loadMusic(byte *data, uint32 size) {
pos += 1;
// And now we're at the actual data. Only one track.
- _num_tracks = 1;
+ _numTracks = 1;
_data = pos;
_tracks[0] = pos;
@@ -194,7 +203,7 @@ bool MidiParser_S1D::loadMusic(byte *data, uint32 size) {
void MidiParser_S1D::resetTracking() {
MidiParser::resetTracking();
// The first event never contains any delta.
- _no_delta = true;
+ _noDelta = true;
memset(_loops, 0, sizeof(_loops));
}
diff --git a/engines/agos/module.mk b/engines/agos/module.mk
index 7ae5e17bf2..7069d8005b 100644
--- a/engines/agos/module.mk
+++ b/engines/agos/module.mk
@@ -51,7 +51,6 @@ ifdef ENABLE_AGOS2
MODULE_OBJS += \
animation.o \
feeble.o \
- installshield_cab.o \
oracle.o \
script_dp.o \
script_ff.o \
diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp
index 0305879390..2e44a6575c 100644
--- a/engines/agos/res.cpp
+++ b/engines/agos/res.cpp
@@ -23,6 +23,8 @@
// Resource file routines for Simon1/Simon2
+#include "common/archive.h"
+#include "common/installshield_cab.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/textconsole.h"
@@ -31,7 +33,6 @@
#include "agos/agos.h"
#include "agos/intern.h"
#include "agos/sound.h"
-#include "agos/installshield_cab.h"
#include "common/zlib.h"
@@ -43,7 +44,10 @@ ArchiveMan::ArchiveMan() {
#ifdef ENABLE_AGOS2
void ArchiveMan::registerArchive(const Common::String &filename, int priority) {
- add(filename, makeInstallShieldArchive(filename), priority);
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(filename);
+
+ if (stream)
+ add(filename, makeInstallShieldArchive(stream, DisposeAfterUse::YES), priority);
}
#endif
diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp
index c6bca1a6e6..e13fa214d1 100644
--- a/engines/agos/saveload.cpp
+++ b/engines/agos/saveload.cpp
@@ -142,7 +142,7 @@ void AGOSEngine_Feeble::quickLoadOrSave() {
}
#endif
-// The function uses segments of code from the original game scripts
+// The function uses segments of code from the original game scripts
// to allow quick loading and saving, but isn't perfect.
//
// Unfortuntely this allows loading and saving in locations,
@@ -1424,7 +1424,7 @@ bool AGOSEngine_Elvira2::loadGame(const char *filename, bool restartMode) {
// The floppy disk versions of Simon the Sorcerer 2 block changing
// to scrolling rooms, if the copy protection fails. But the copy
// protection flags are never set in the CD version.
- // Setting this copy protection flag, allows saved games to be shared
+ // Setting this copy protection flag, allows saved games to be shared
// between all versions of Simon the Sorcerer 2.
if (getGameType() == GType_SIMON2) {
setBitFlag(135, 1);
diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp
index 85c449eafc..bec41bbbd3 100644
--- a/engines/agos/sound.cpp
+++ b/engines/agos/sound.cpp
@@ -297,7 +297,7 @@ Audio::AudioStream *RawSound::makeAudioStream(uint sound) {
warning("RawSound::makeAudioStream: Could not open file \"%s\"", _filename.c_str());
return NULL;
}
-
+
file->seek(_offsets[sound], SEEK_SET);
uint size = file->readUint32BE();
return Audio::makeRawStream(new Common::SeekableSubReadStream(file, _offsets[sound] + 4, _offsets[sound] + 4 + size, DisposeAfterUse::YES), 22050, _flags, DisposeAfterUse::YES);
diff --git a/engines/cge/bitmap.cpp b/engines/cge/bitmap.cpp
index 4f85957b3d..7089c8e0d1 100644
--- a/engines/cge/bitmap.cpp
+++ b/engines/cge/bitmap.cpp
@@ -94,7 +94,7 @@ Bitmap::Bitmap(CGEEngine *vm, uint16 w, uint16 h, uint8 fill)
// Replicate across the entire table
for (HideDesc *hdP = b + 1; hdP < (b + _h); hdP++)
*hdP = *b;
-
+
b->_skip = 0; // fix the first entry
_v = v;
_b = b;
@@ -357,7 +357,7 @@ bool Bitmap::loadVBM(EncryptedStream *f) {
// Read in the palette
byte palData[kPalSize];
f->read(palData, kPalSize);
-
+
const byte *srcP = palData;
for (int idx = 0; idx < kPalCount; idx++, srcP += 3) {
_vm->_bitmapPalette[idx]._r = *srcP;
diff --git a/engines/cge/cge_main.cpp b/engines/cge/cge_main.cpp
index 3ba5f7fed9..f4f1cd3e0b 100644
--- a/engines/cge/cge_main.cpp
+++ b/engines/cge/cge_main.cpp
@@ -207,7 +207,7 @@ bool CGEEngine::loadGame(int slotNumber, SavegameHeader *header, bool tiny) {
readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
} else {
- // Open up the savgame file
+ // Open up the savegame file
Common::String slotName = generateSaveName(slotNumber);
Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
@@ -280,7 +280,7 @@ Common::Error CGEEngine::loadGameState(int slot) {
sceneDown();
_hero->park();
resetGame();
-
+
// If music is playing, kill it.
if (_music)
_midiPlayer->killMidi();
@@ -316,8 +316,8 @@ Common::Error CGEEngine::saveGameState(int slot, const Common::String &desc) {
// Reload the scene
sceneUp();
- _hero->_x = x;
- _hero->_y = y;
+ // Restore player position
+ _hero->gotoxy(x, y);
_hero->_z = z;
return Common::kNoError;
@@ -508,7 +508,7 @@ void CGEEngine::loadMapping() {
if (!cf.err()) {
// Move to the data for the given room
cf.seek((_now - 1) * kMapArrSize);
-
+
// Read in the data
for (int z = 0; z < kMapZCnt; ++z) {
cf.read(&_clusterMap[z][0], kMapXCnt);
@@ -772,7 +772,7 @@ void System::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) {
if (mask & kEventKeyb) {
if (keyCode == Common::KEYCODE_ESCAPE) {
- // The original was calling keyClick()
+ // The original was calling keyClick()
// The sound is uselessly annoying and noisy, so it has been removed
_vm->killText();
if (_vm->_startupMode == 1) {
@@ -1044,7 +1044,7 @@ void CGEEngine::loadSprite(const char *fname, int ref, int scene, int col = 0, i
uint16 len;
for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) {
- len = line.size();
+ len = line.size();
lcnt++;
strcpy(tmpStr, line.c_str());
if (len == 0 || *tmpStr == '.')
diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp
index 2e04b82026..3d6c24d68b 100644
--- a/engines/cge/detection.cpp
+++ b/engines/cge/detection.cpp
@@ -198,7 +198,7 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const {
SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String fileName = Common::String::format("%s.%03d", target, slot);
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
-
+
if (f) {
CGE::SavegameHeader header;
@@ -229,7 +229,7 @@ SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int sl
return desc;
}
}
-
+
return SaveStateDescriptor();
}
diff --git a/engines/cge/events.h b/engines/cge/events.h
index 522aa67905..ab8d87212d 100644
--- a/engines/cge/events.h
+++ b/engines/cge/events.h
@@ -105,7 +105,7 @@ private:
void handleEvents();
public:
EventManager(CGEEngine *vm);
- void poll();
+ void poll();
void clearEvent(Sprite *spr);
CGEEvent &getNextEvent();
diff --git a/engines/cge/fileio.cpp b/engines/cge/fileio.cpp
index f23105d823..609d5e86aa 100644
--- a/engines/cge/fileio.cpp
+++ b/engines/cge/fileio.cpp
@@ -98,7 +98,7 @@ uint16 ResourceManager::XCrypt(void *buf, uint16 length) {
for (uint16 i = 0; i < length; i++)
*b++ ^= kCryptSeed;
-
+
return kCryptSeed;
}
diff --git a/engines/cge/text.cpp b/engines/cge/text.cpp
index fd4120d49d..a8ce8777c5 100644
--- a/engines/cge/text.cpp
+++ b/engines/cge/text.cpp
@@ -63,7 +63,7 @@ int16 Text::count() {
Common::String line;
char tmpStr[kLineMax + 1];
-
+
int counter = 0;
for (line = tf.readLine(); !tf.eos(); line = tf.readLine()) {
diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp
index 60168831a1..075a59cfb6 100644
--- a/engines/cine/anim.cpp
+++ b/engines/cine/anim.cpp
@@ -202,13 +202,13 @@ AnimData::AnimData(const AnimData &src) : _width(src._width),
if (src._data) {
_data = new byte[_size];
assert(_data);
- memcpy(_data, src._data, _size*sizeof(byte));
+ memcpy(_data, src._data, _size * sizeof(byte));
}
if (src._mask) {
_mask = new byte[_size];
assert(_mask);
- memcpy(_mask, src._mask, _size*sizeof(byte));
+ memcpy(_mask, src._mask, _size * sizeof(byte));
}
memset(_name, 0, sizeof(_name));
@@ -272,8 +272,7 @@ byte AnimData::getColor(int x, int y) {
* @param transparent Transparent color (for ANIM_MASKSPRITE)
*/
void AnimData::load(byte *d, int type, uint16 w, uint16 h, int16 file,
- int16 frame, const char *n, byte transparent) {
-
+ int16 frame, const char *n, byte transparent) {
assert(d);
if (_data) {
@@ -299,7 +298,7 @@ void AnimData::load(byte *d, int type, uint16 w, uint16 h, int16 file,
_size = w * h;
_data = new byte[_size];
assert(_data);
- memcpy(_data, d, _size*sizeof(byte));
+ memcpy(_data, d, _size * sizeof(byte));
break;
case ANIM_MASK:
@@ -536,7 +535,7 @@ int loadSpl(const char *resourceName, int16 idx) {
entry = idx < 0 ? emptyAnimSpace() : idx;
assert(entry >= 0);
- g_cine->_animDataTable[entry].load(dataPtr, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName);
+ g_cine->_animDataTable[entry].load(dataPtr + 0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize - 0x16, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
return entry + 1;
@@ -546,9 +545,10 @@ int loadSpl(const char *resourceName, int16 idx) {
* Load 1bpp mask
* @param resourceName Mask filename
* @param idx Target index in animDataTable (-1 if any empty space will do)
+ * @param frameIndex frame of animation to load (-1 for all frames)
* @return The number of the animDataTable entry after the loaded mask (-1 if error)
*/
-int loadMsk(const char *resourceName, int16 idx) {
+int loadMsk(const char *resourceName, int16 idx, int16 frameIndex) {
int16 foundFileIdx = findFileInBundle(resourceName);
if (foundFileIdx < 0) {
return -1;
@@ -563,9 +563,18 @@ int loadMsk(const char *resourceName, int16 idx) {
loadAnimHeader(animHeader, readS);
ptr = dataPtr + 0x16;
+ int16 startFrame = 0;
+ int16 endFrame = animHeader.numFrames;
+
+ if (frameIndex >= 0) {
+ startFrame = frameIndex;
+ endFrame = frameIndex + 1;
+ ptr += frameIndex * animHeader.frameWidth * animHeader.frameHeight;
+ }
+
entry = idx < 0 ? emptyAnimSpace() : idx;
assert(entry >= 0);
- for (int16 i = 0; i < animHeader.numFrames; i++, entry++) {
+ for (int16 i = startFrame; i < endFrame; i++, entry++) {
g_cine->_animDataTable[entry].load(ptr, ANIM_MASK, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName);
ptr += animHeader.frameWidth * animHeader.frameHeight;
}
@@ -578,9 +587,10 @@ int loadMsk(const char *resourceName, int16 idx) {
* Load animation
* @param resourceName Animation filename
* @param idx Target index in animDataTable (-1 if any empty space will do)
+ * @param frameIndex frame of animation to load (-1 for all frames)
* @return The number of the animDataTable entry after the loaded animation (-1 if error)
*/
-int loadAni(const char *resourceName, int16 idx) {
+int loadAni(const char *resourceName, int16 idx, int16 frameIndex) {
int16 foundFileIdx = findFileInBundle(resourceName);
if (foundFileIdx < 0) {
return -1;
@@ -596,6 +606,15 @@ int loadAni(const char *resourceName, int16 idx) {
loadAnimHeader(animHeader, readS);
ptr = dataPtr + 0x16;
+ int16 startFrame = 0;
+ int16 endFrame = animHeader.numFrames;
+
+ if (frameIndex >= 0) {
+ startFrame = frameIndex;
+ endFrame = frameIndex + 1;
+ ptr += frameIndex * animHeader.frameWidth * animHeader.frameHeight;
+ }
+
transparentColor = getAnimTransparentColor(resourceName);
// TODO: Merge this special case hack into getAnimTransparentColor somehow.
@@ -609,7 +628,7 @@ int loadAni(const char *resourceName, int16 idx) {
entry = idx < 0 ? emptyAnimSpace() : idx;
assert(entry >= 0);
- for (int16 i = 0; i < animHeader.numFrames; i++, entry++) {
+ for (int16 i = startFrame; i < endFrame; i++, entry++) {
// special case transparency handling
if (!strcmp(resourceName, "L2202.ANI")) {
transparentColor = i < 2 ? 0 : 7;
@@ -669,13 +688,13 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) {
*(source + k) <<= 1;
if (k > 0 + m)
color <<= 1;
- } // end k
+ } // end k
*(dest++) = color;
- } // end i
- } // end m
+ } // end i
+ } // end m
source += 0x10;
- } // end j
+ } // end j
}
/**
@@ -685,7 +704,7 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) {
* @param frameIndex frame of animation to load (-1 for all frames)
* @return The number of the animDataTable entry after the loaded image set (-1 if error)
*/
-int loadSet(const char *resourceName, int16 idx, int16 frameIndex =-1 ) {
+int loadSet(const char *resourceName, int16 idx, int16 frameIndex = -1) {
AnimHeader2Struct header2;
uint16 numSpriteInAnim;
int16 foundFileIdx = findFileInBundle(resourceName);
@@ -712,10 +731,9 @@ int loadSet(const char *resourceName, int16 idx, int16 frameIndex =-1 ) {
int16 startFrame = 0;
int16 endFrame = numSpriteInAnim;
- if(frameIndex>=0)
- {
+ if (frameIndex >= 0) {
startFrame = frameIndex;
- endFrame = frameIndex+1;
+ endFrame = frameIndex + 1;
ptr += 0x10 * frameIndex;
}
@@ -766,7 +784,7 @@ int loadSeq(const char *resourceName, int16 idx) {
byte *dataPtr = readBundleFile(foundFileIdx);
int entry = idx < 0 ? emptyAnimSpace() : idx;
- g_cine->_animDataTable[entry].load(dataPtr+0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize-0x16, 1, foundFileIdx, 0, currentPartName);
+ g_cine->_animDataTable[entry].load(dataPtr + 0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize - 0x16, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
return entry + 1;
}
@@ -783,11 +801,11 @@ int loadResource(const char *resourceName, int16 idx, int16 frameIndex) {
if (strstr(resourceName, ".SPL")) {
result = loadSpl(resourceName, idx);
} else if (strstr(resourceName, ".MSK")) {
- result = loadMsk(resourceName, idx);
+ result = loadMsk(resourceName, idx, frameIndex);
} else if (strstr(resourceName, ".ANI")) {
- result = loadAni(resourceName, idx);
+ result = loadAni(resourceName, idx, frameIndex);
} else if (strstr(resourceName, ".ANM")) {
- result = loadAni(resourceName, idx);
+ result = loadAni(resourceName, idx, frameIndex);
} else if (strstr(resourceName, ".SET")) {
result = loadSet(resourceName, idx, frameIndex);
} else if (strstr(resourceName, ".SEQ")) {
diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp
index bbe2cd4896..aa7221f733 100644
--- a/engines/cine/cine.cpp
+++ b/engines/cine/cine.cpp
@@ -189,7 +189,18 @@ void CineEngine::initialize() {
g_cine->_messageTable.clear();
resetObjectTable();
- disableSystemMenu = 1;
+ if (getGameType() == Cine::GType_OS) {
+ disableSystemMenu = 1;
+ } else {
+ // WORKAROUND: We do not save this variable in FW's savegames.
+ // Initializing this to 1, like we do it in the OS case, will
+ // cause the menu disabled when loading from the launcher or
+ // command line.
+ // A proper fix here would be to save this variable in FW's saves.
+ // Since it seems these are unversioned so far, there would be need
+ // to properly add versioning to them first.
+ disableSystemMenu = 0;
+ }
var8 = 0;
diff --git a/engines/cine/cine.h b/engines/cine/cine.h
index 55376dce29..47edf51c30 100644
--- a/engines/cine/cine.h
+++ b/engines/cine/cine.h
@@ -159,7 +159,7 @@ private:
bool _preLoad;
int _timerDelayMultiplier;
- public:
+public:
// TODO: These are pseudo-global vars
// They better belong to appropriate classes
Common::Array<AnimData> _animDataTable;
diff --git a/engines/cine/console.cpp b/engines/cine/console.cpp
index 0a24b2408a..4af28592e7 100644
--- a/engines/cine/console.cpp
+++ b/engines/cine/console.cpp
@@ -28,7 +28,7 @@ namespace Cine {
bool labyrinthCheat;
CineConsole::CineConsole(CineEngine *vm) : GUI::Debugger(), _vm(vm) {
- DCmd_Register("labyrinthCheat", WRAP_METHOD(CineConsole, Cmd_LabyrinthCheat));
+ DCmd_Register("labyrinthCheat", WRAP_METHOD(CineConsole, Cmd_LabyrinthCheat));
labyrinthCheat = false;
}
diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp
index 7a988227f6..636c0cf8d9 100644
--- a/engines/cine/gfx.cpp
+++ b/engines/cine/gfx.cpp
@@ -113,7 +113,7 @@ FWRenderer::FWRenderer() : _background(NULL), _backupPal(), _cmd(""),
assert(_backBuffer);
memset(_backBuffer, 0, _screenSize);
- memset(_bgName, 0, sizeof (_bgName));
+ memset(_bgName, 0, sizeof(_bgName));
}
@@ -249,7 +249,7 @@ void FWRenderer::drawCommand() {
unsigned int i;
int x = 10, y = _cmdY;
- if(disableSystemMenu == 0) {
+ if (disableSystemMenu == 0) {
drawPlainBox(x, y, 301, 11, 0);
drawBorder(x - 1, y - 1, 302, 12, 2);
@@ -307,7 +307,7 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color
i++;
line = fitLine(str + i, tw, words, cw);
- if ( str[i + line] != '\0' && str[i + line] != 0x7C && words) {
+ if (str[i + line] != '\0' && str[i + line] != 0x7C && words) {
space = (tw - cw) / words;
extraSpace = (tw - cw) % words;
} else {
@@ -471,6 +471,41 @@ int FWRenderer::drawChar(char character, int x, int y) {
return x;
}
+/**
+ * Clears the character glyph to black
+ * This function is called "undrawChar", because the original only applies
+ * this drawing after the original glyph has been drawn.
+ * Possible TODO: Find a better name.
+ * @param character Character to undraw
+ * @param x Character coordinate
+ * @param y Character coordinate
+ */
+int FWRenderer::undrawChar(char character, int x, int y) {
+ int width, idx;
+
+ if (character == ' ') {
+ x += 5;
+ } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) {
+ idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx;
+ const byte *sprite = g_cine->_textHandler.textTable[idx][FONT_DATA];
+ for (uint i = 0; i < FONT_HEIGHT; ++i) {
+ byte *dst = _backBuffer + (y + i) * 320 + x;
+ for (uint j = 0; j < FONT_WIDTH; ++j, ++dst) {
+ // The original does this based on whether bit 1 of the pixel
+ // is set. Since that's the only bit ever set in (FW) this
+ // check should be fine.
+ // TODO: Check how Operation Stealth Amiga works
+ if (*sprite++) {
+ *dst = 0;
+ }
+ }
+ }
+ x += width + 1;
+ }
+
+ return x;
+}
+
int FWRenderer::getStringWidth(const char *str) {
const char *p = str;
int width = 0;
@@ -969,20 +1004,29 @@ void SelectionMenu::drawMenu(FWRenderer &r, bool top) {
charX = x + 4;
if (i == _selection) {
+ int color;
+
if (isAmiga) {
- // The original Amiga version is using a different highlight color here,
- // but with our current code it is not possible to change the text color,
- // thus we can not use the Amiga's color, since otherwise the text
- // wouldn't be visible anymore.
- r.drawPlainBox(charX, lineY, _width - 8, FONT_HEIGHT, top ? r._messageBg/*2*/ : 18);
+ if (top) {
+ color = 2;
+ } else {
+ color = 18;
+ }
} else {
- r.drawPlainBox(charX, lineY, _width - 8, 9, 0);
+ color = 0;
}
+
+ r.drawPlainBox(x + 2, lineY - 1, _width - 3, 9, color);
}
const int size = _elements[i].size();
- for (int j = 0; j < size; ++j)
- charX = r.drawChar(_elements[i][j], charX, lineY);
+ for (int j = 0; j < size; ++j) {
+ if (isAmiga && i == _selection) {
+ charX = r.undrawChar(_elements[i][j], charX, lineY);
+ } else {
+ charX = r.drawChar(_elements[i][j], charX, lineY);
+ }
+ }
}
}
@@ -1244,6 +1288,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame];
drawSprite(&(*it), sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, g_cine->_objectTable[it->objIdx].x, g_cine->_objectTable[it->objIdx].y, g_cine->_objectTable[it->objIdx].part, sprite->_bpp);
break;
+
// game message
case 2:
if (it->objIdx >= g_cine->_messageTable.size()) {
@@ -1300,7 +1345,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
height = obj->costume;
drawPlainBox(obj->x, obj->y, width, height, color);
debug(5, "renderOverlay: type=%d, x=%d, y=%d, width=%d, height=%d, color=%d",
- it->type, obj->x, obj->y, width, height, color);
+ it->type, obj->x, obj->y, width, height, color);
break;
// something else
@@ -1424,7 +1469,7 @@ void OSRenderer::selectBg(unsigned int idx) {
if (_bgTable[idx].bg) {
assert(_bgTable[idx].pal.isValid() && !(_bgTable[idx].pal.empty()));
- _currentBg = idx;
+ _currentBg = idx;
} else
warning("OSRenderer::selectBg(%d) - attempt to select null background", idx);
reloadPalette();
@@ -1750,23 +1795,23 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi
// draw the mask based on next objects in the list
Common::List<overlay>::iterator it;
- for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
- if(&(*it) == overlayPtr) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
+ if (&(*it) == overlayPtr) {
break;
}
}
- while(it != g_cine->_overlayList.end()) {
+ while (it != g_cine->_overlayList.end()) {
overlay *pCurrentOverlay = &(*it);
if ((pCurrentOverlay->type == 5) || ((pCurrentOverlay->type == 21) && (pCurrentOverlay->x == overlayPtr->objIdx))) {
AnimData *sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame];
if (pMask == NULL) {
- pMask = new byte[width*height];
+ pMask = new byte[width * height];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
- byte spriteColor= spritePtr[width * i + j];
+ byte spriteColor = spritePtr[width * i + j];
pMask[width * i + j] = spriteColor;
}
}
@@ -1777,7 +1822,7 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi
int inMaskX = (g_cine->_objectTable[it->objIdx].x + i) - x;
int inMaskY = (g_cine->_objectTable[it->objIdx].y + j) - y;
- if (inMaskX >=0 && inMaskX < width) {
+ if (inMaskX >= 0 && inMaskX < width) {
if (inMaskY >= 0 && inMaskY < height) {
if (sprite->_bpp == 1) {
if (!sprite->getColor(i, j)) {
@@ -1793,7 +1838,7 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi
}
// now, draw with the mask we created
- if(pMask) {
+ if (pMask) {
spritePtr = pMask;
}
@@ -1808,7 +1853,7 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi
destPtr += i * 320;
for (int j = 0; j < width; j++) {
- byte color= *(spritePtr++);
+ byte color = *(spritePtr++);
if ((transparentColor != color) && x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200) {
*(destPtr++) = color;
} else {
diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h
index 3434cf9fc2..8b8843fd72 100644
--- a/engines/cine/gfx.h
+++ b/engines/cine/gfx.h
@@ -152,6 +152,7 @@ protected:
void drawBorder(int x, int y, int width, int height, byte color);
void drawDoubleBorder(int x, int y, int width, int height, byte color);
virtual int drawChar(char character, int x, int y);
+ virtual int undrawChar(char character, int x, int y);
void drawLine(int x, int y, int width, int height, byte color);
void remaskSprite(byte *mask, Common::List<overlay>::iterator it);
virtual void drawBackground();
@@ -287,7 +288,7 @@ byte gfxGetColor(int16 x, int16 y, const byte *ptr, int16 width);
void gfxResetRawPage(byte *pageRaw);
void gfxConvertSpriteToRaw(byte *dst, const byte *src, uint16 w, uint16 h);
-void gfxCopyRawPage(byte *source, byte * dest);
+void gfxCopyRawPage(byte *source, byte *dest);
void gfxFlipRawPage(byte *frontBuffer);
void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int16 height, byte *page, int16 x, int16 y);
void gfxDrawPlainBoxRaw(int16 x1, int16 y1, int16 x2, int16 y2, byte color, byte *page);
diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp
index f13f38a45e..c822f1cabd 100644
--- a/engines/cine/main_loop.cpp
+++ b/engines/cine/main_loop.cpp
@@ -180,19 +180,19 @@ static void processEvent(Common::Event &event) {
case Common::KEYCODE_F11:
renderer->showCollisionPage(false);
break;
- case Common::KEYCODE_KP5: // Emulated left mouse button click
- case Common::KEYCODE_LEFT: // Left
- case Common::KEYCODE_KP4: // Left
+ case Common::KEYCODE_KP5: // Emulated left mouse button click
+ case Common::KEYCODE_LEFT: // Left
+ case Common::KEYCODE_KP4: // Left
case Common::KEYCODE_RIGHT: // Right
- case Common::KEYCODE_KP6: // Right
- case Common::KEYCODE_UP: // Up
- case Common::KEYCODE_KP8: // Up
- case Common::KEYCODE_DOWN: // Down
- case Common::KEYCODE_KP2: // Down
- case Common::KEYCODE_KP9: // Up & Right
- case Common::KEYCODE_KP7: // Up & Left
- case Common::KEYCODE_KP1: // Down & Left
- case Common::KEYCODE_KP3: // Down & Right
+ case Common::KEYCODE_KP6: // Right
+ case Common::KEYCODE_UP: // Up
+ case Common::KEYCODE_KP8: // Up
+ case Common::KEYCODE_DOWN: // Down
+ case Common::KEYCODE_KP2: // Down
+ case Common::KEYCODE_KP9: // Up & Right
+ case Common::KEYCODE_KP7: // Up & Left
+ case Common::KEYCODE_KP1: // Down & Left
+ case Common::KEYCODE_KP3: // Down & Right
// Stop ego movement made with keyboard when releasing a known key
moveUsingKeyboard(0, 0);
break;
@@ -217,7 +217,6 @@ void manageEvents() {
g_system->delayMillis(20);
} while (g_system->getMillis() < nextFrame);
- g_sound->update();
mouseData.left = mouseLeft;
mouseData.right = mouseRight;
}
@@ -434,9 +433,9 @@ void CineEngine::mainLoop(int bootScriptIdx) {
hideMouse();
g_sound->stopMusic();
- // if (g_cine->getGameType() == Cine::GType_OS) {
+ //if (g_cine->getGameType() == Cine::GType_OS) {
// freeUnkList();
- // }
+ //}
closePart();
}
diff --git a/engines/cine/object.cpp b/engines/cine/object.cpp
index afd95c04b0..a75828abb1 100644
--- a/engines/cine/object.cpp
+++ b/engines/cine/object.cpp
@@ -59,7 +59,7 @@ void loadObject(char *pObjectName) {
assert(numEntry <= NUM_MAX_OBJECT);
for (i = 0; i < numEntry; i++) {
- if (g_cine->_objectTable[i].costume != -2 && g_cine->_objectTable[i].costume != -3) { // flag is keep ?
+ if (g_cine->_objectTable[i].costume != -2 && g_cine->_objectTable[i].costume != -3) { // flag is keep?
Common::MemoryReadStream readS(ptr, entrySize);
g_cine->_objectTable[i].x = readS.readSint16BE();
diff --git a/engines/cine/pal.cpp b/engines/cine/pal.cpp
index 779c279ea1..10077ecdc9 100644
--- a/engines/cine/pal.cpp
+++ b/engines/cine/pal.cpp
@@ -92,7 +92,8 @@ void loadRelatedPalette(const char *fileName) {
paletteIndex = findPaletteFromName(localName);
if (paletteIndex == -1) {
- for (i = 0; i < 16; i++) { // generate default palette
+ // generate default palette
+ for (i = 0; i < 16; i++) {
paletteBuffer1[i] = paletteBuffer2[i] = (i << 4) + i;
}
} else {
diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp
index 03cb743b46..813cbe50af 100644
--- a/engines/cine/part.cpp
+++ b/engines/cine/part.cpp
@@ -263,7 +263,7 @@ byte *readBundleSoundFile(const char *entryName, uint32 *size) {
/** Rotate byte value to the left by n bits */
byte rolByte(byte value, uint n) {
n %= 8;
- return (byte) ((value << n) | (value >> (8 - n)));
+ return (byte)((value << n) | (value >> (8 - n)));
}
byte *readFile(const char *filename, bool crypted) {
diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp
index 20952eea52..51d2c1f6be 100644
--- a/engines/cine/saveload.cpp
+++ b/engines/cine/saveload.cpp
@@ -1002,7 +1002,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam
const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30);
const int fileStartPos = fHandle.pos();
- for(int resourceIndex=0; resourceIndex<NUM_MAX_ANIMDATA; resourceIndex++) {
+ for (int resourceIndex = 0; resourceIndex < NUM_MAX_ANIMDATA; resourceIndex++) {
// Seek to the start of the current animation's entry
fHandle.seek(fileStartPos + resourceIndex * entrySize);
// Read in the current animation entry
diff --git a/engines/cine/saveload.h b/engines/cine/saveload.h
index 49c9c0cef7..fd661904af 100644
--- a/engines/cine/saveload.h
+++ b/engines/cine/saveload.h
@@ -68,7 +68,7 @@ enum CineSaveGameFormat {
};
/** Identifier for the temporary Operation Stealth savegame format. */
-static const uint32 TEMP_OS_FORMAT_ID = MKTAG('T','E','M','P');
+static const uint32 TEMP_OS_FORMAT_ID = MKTAG('T', 'E', 'M', 'P');
/** The current version number of Operation Stealth's savegame format. */
static const uint32 CURRENT_OS_SAVE_VER = 1;
diff --git a/engines/cine/script.h b/engines/cine/script.h
index 3fc86c585b..a07c8d6cfc 100644
--- a/engines/cine/script.h
+++ b/engines/cine/script.h
@@ -227,6 +227,7 @@ protected:
int o1_op72();
int o1_op73();
int o1_playSample();
+ int o1_playSampleSwapped();
int o1_disableSystemMenu();
int o1_loadMask5();
int o1_unloadMask5();
diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp
index 9cbe3c3fab..b4fe68c343 100644
--- a/engines/cine/script_fw.cpp
+++ b/engines/cine/script_fw.cpp
@@ -196,7 +196,7 @@ void FWScript::setupTable() {
{ 0, 0 },
{ &FWScript::o1_playSample, "bbwbww" },
/* 78 */
- { &FWScript::o1_playSample, "bbwbww" },
+ { &FWScript::o1_playSampleSwapped, "bbwbww" },
{ &FWScript::o1_disableSystemMenu, "b" },
{ &FWScript::o1_loadMask5, "b" },
{ &FWScript::o1_unloadMask5, "b" }
@@ -352,7 +352,7 @@ void ScriptVars::load(Common::SeekableReadStream &fHandle, unsigned int len) {
* Reset all values to 0
*/
void ScriptVars::reset() {
- memset( _vars, 0, _size * sizeof(int16));
+ memset(_vars, 0, _size * sizeof(int16));
}
/**
@@ -380,10 +380,10 @@ RawScript::RawScript(const FWScriptInfo &info, const byte *data, uint16 s) :
* Copy constructor
*/
RawScript::RawScript(const RawScript &src) : _size(src._size),
- _data(new byte[_size+1]), _labels(src._labels) {
+ _data(new byte[_size + 1]), _labels(src._labels) {
assert(_data);
- memcpy(_data, src._data, _size+1);
+ memcpy(_data, src._data, _size + 1);
}
/**
@@ -398,7 +398,7 @@ RawScript::~RawScript() {
*/
RawScript &RawScript::operator=(const RawScript &src) {
assert(src._data);
- byte *tmp = new byte[src._size+1];
+ byte *tmp = new byte[src._size + 1];
assert(tmp);
_labels = src._labels;
@@ -443,14 +443,14 @@ int RawScript::getNextLabel(const FWScriptInfo &info, int offset) const {
pos += 2;
break;
case 'c': { // byte != 0 ? byte : word
- uint8 test = _data[pos];
+ uint8 test = _data[pos];
+ pos++;
+ if (test) {
pos++;
- if (test) {
- pos++;
- } else {
- pos += 2;
- }
+ } else {
+ pos += 2;
}
+ }
break;
case 'l': // label
return pos;
@@ -459,7 +459,7 @@ int RawScript::getNextLabel(const FWScriptInfo &info, int offset) const {
;
break;
case 'x': // exit script
- return -pos-1;
+ return -pos - 1;
}
}
}
@@ -498,9 +498,7 @@ void RawScript::computeLabels(const FWScriptInfo &info) {
*
* computeScriptStackFromScript replacement
*/
-uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset)
- const {
-
+uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset) const {
assert(_data);
int pos = offset;
@@ -519,7 +517,7 @@ uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset)
*/
void RawScript::setData(const FWScriptInfo &info, const byte *data) {
assert(!_data); // this function should be called only once per instance
- _data = new byte[_size+1];
+ _data = new byte[_size + 1];
assert(data && _data);
memcpy(_data, data, _size * sizeof(byte));
@@ -553,7 +551,7 @@ byte RawScript::getByte(unsigned int pos) const {
* @return Word of bytecode
*/
uint16 RawScript::getWord(unsigned int pos) const {
- assert(_data && pos+1 < _size);
+ assert(_data && pos + 1 < _size);
return READ_BE_UINT16(_data + pos);
}
@@ -566,7 +564,7 @@ uint16 RawScript::getWord(unsigned int pos) const {
const char *RawScript::getString(unsigned int pos) const {
assert(_data && pos < _size);
- return (const char*)(_data+pos);
+ return (const char *)(_data + pos);
}
/**
@@ -580,8 +578,8 @@ const char *RawScript::getString(unsigned int pos) const {
* instance can be used. It leaves the instance in partially invalid state.
*/
RawObjectScript::RawObjectScript(uint16 s, uint16 p1, uint16 p2, uint16 p3)
- : RawScript(s), _runCount(0), _param1(p1), _param2(p2), _param3(p3)
-{ }
+ : RawScript(s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) {
+}
/**
* Complete constructor
@@ -592,8 +590,9 @@ RawObjectScript::RawObjectScript(uint16 s, uint16 p1, uint16 p2, uint16 p3)
* @param p3 Third object script parameter
*/
RawObjectScript::RawObjectScript(const FWScriptInfo &info, const byte *data,
- uint16 s, uint16 p1, uint16 p2, uint16 p3) : RawScript(info, data, s),
- _runCount(0), _param1(p1), _param2(p2), _param3(p3) { }
+ uint16 s, uint16 p1, uint16 p2, uint16 p3)
+ : RawScript(info, data, s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) {
+}
/**
* Contructor for global scripts
@@ -603,7 +602,8 @@ RawObjectScript::RawObjectScript(const FWScriptInfo &info, const byte *data,
FWScript::FWScript(const RawScript &script, int16 idx) : _script(script),
_pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
- _globalVars(g_cine->_globalVars), _info(new FWScriptInfo) { }
+ _globalVars(g_cine->_globalVars), _info(new FWScriptInfo) {
+}
/**
* Copy constructor
@@ -611,25 +611,27 @@ FWScript::FWScript(const RawScript &script, int16 idx) : _script(script),
FWScript::FWScript(const FWScript &src) : _script(src._script), _pos(src._pos),
_line(src._line), _compare(src._compare), _index(src._index),
_labels(src._labels), _localVars(src._localVars),
- _globalVars(src._globalVars), _info(new FWScriptInfo) { }
+ _globalVars(src._globalVars), _info(new FWScriptInfo) {
+}
/**
* Contructor for global scripts in derived classes
* @param script Script bytecode reference
* @param idx Script bytecode index
*/
-FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info) :
- _script(script), _pos(0), _line(0), _compare(0), _index(idx),
+FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info)
+ : _script(script), _pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
- _globalVars(g_cine->_globalVars), _info(info) { }
+ _globalVars(g_cine->_globalVars), _info(info) {
+}
/**
* Constructor for object scripts in derived classes
* @param script Script bytecode reference
* @param idx Script bytecode index
*/
-FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) :
- _script(script), _pos(0), _line(0), _compare(0), _index(idx),
+FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info)
+ : _script(script), _pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
_globalVars(g_cine->_globalVars), _info(info) {
@@ -639,8 +641,8 @@ FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) :
/**
* Copy constructor for derived classes
*/
-FWScript::FWScript(const FWScript &src, FWScriptInfo *info) :
- _script(src._script), _pos(src._pos), _line(src._line),
+FWScript::FWScript(const FWScript &src, FWScriptInfo *info)
+ : _script(src._script), _pos(src._pos), _line(src._line),
_compare(src._compare), _index(src._index), _labels(src._labels),
_localVars(src._localVars), _globalVars(src._globalVars), _info(info) { }
@@ -704,7 +706,7 @@ void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 co
int FWScript::execute() {
int ret = 0;
- if(_script._size) {
+ if (_script._size) {
while (!ret) {
_line = _pos;
byte opcode = getNextByte();
@@ -1816,6 +1818,9 @@ int FWScript::o1_playSample() {
if (g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) {
if (size == 0xFFFF) {
size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
+ } else if (size > g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height) {
+ warning("o1_playSample: Got invalid sample size %d for sample %d", size, anim);
+ size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
}
if (channel < 10) { // || _currentOpcode == 0x78
int channel1, channel2;
@@ -1823,8 +1828,8 @@ int FWScript::o1_playSample() {
channel1 = 0;
channel2 = 1;
} else {
- channel1 = 2;
- channel2 = 3;
+ channel1 = 3;
+ channel2 = 2;
}
g_sound->playSound(channel1, freq, data, size, -1, volume, 63, repeat);
g_sound->playSound(channel2, freq, data, size, 1, volume, 0, repeat);
@@ -1858,6 +1863,53 @@ int FWScript::o1_playSample() {
return 0;
}
+int FWScript::o1_playSampleSwapped() {
+ // TODO: The DOS version probably does not have any stereo support here
+ // since the only stereo output it supports should be the Roland MT-32.
+ // So it probably does the same as o1_playSample here. Checking this will
+ // be a good idea never the less.
+ if (g_cine->getPlatform() == Common::kPlatformPC) {
+ return o1_playSample();
+ }
+
+ debugC(5, kCineDebugScript, "Line: %d: playSampleInversed()", _line);
+
+ byte anim = getNextByte();
+ byte channel = getNextByte();
+
+ uint16 freq = getNextWord();
+ byte repeat = getNextByte();
+
+ int16 volume = getNextWord();
+ uint16 size = getNextWord();
+
+ const byte *data = g_cine->_animDataTable[anim].data();
+
+ if (!data) {
+ return 0;
+ }
+
+ if (size == 0xFFFF) {
+ size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
+ } else if (size > g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height) {
+ warning("o1_playSampleSwapped: Got invalid sample size %d for sample %d", size, anim);
+ size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
+ }
+
+ int channel1, channel2;
+ if (channel == 0) {
+ channel1 = 1;
+ channel2 = 0;
+ } else {
+ channel1 = 2;
+ channel2 = 3;
+ }
+
+ g_sound->playSound(channel1, freq, data, size, -1, volume, 63, repeat);
+ g_sound->playSound(channel2, freq, data, size, 1, volume, 0, repeat);
+ return 0;
+}
+
int FWScript::o1_disableSystemMenu() {
byte param = getNextByte();
@@ -2074,1034 +2126,970 @@ void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx)
strcpy(lineBuffer, "");
switch (opcode - 1) {
- case -1:
- {
- break;
- }
- case 0x0:
- {
- byte param1;
- byte param2;
- int16 param3;
+ case -1: {
+ break;
+ }
+ case 0x0: {
+ byte param1;
+ byte param2;
+ int16 param3;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- sprintf(lineBuffer, "obj[%d]%s = %d\n", param1, getObjPramName(param2), param3);
+ sprintf(lineBuffer, "obj[%d]%s = %d\n", param1, getObjPramName(param2), param3);
- break;
- }
- case 0x1:
- {
- byte param1;
- byte param2;
- byte param3;
+ break;
+ }
+ case 0x1: {
+ byte param1;
+ byte param2;
+ byte param3;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- param3 = *(localScriptPtr + position);
- position++;
+ param3 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "var[%d]=obj[%d]%s\n", param3, param1, getObjPramName(param2));
- break;
- }
+ sprintf(lineBuffer, "var[%d]=obj[%d]%s\n", param3, param1, getObjPramName(param2));
+ break;
+ }
case 0x2:
case 0x3:
case 0x4:
case 0x5:
- case 0x6:
- {
- byte param1;
- byte param2;
- int16 param3;
-
- param1 = *(localScriptPtr + position);
- position++;
-
- param2 = *(localScriptPtr + position);
- position++;
-
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
-
- if (opcode - 1 == 0x2) {
- sprintf(lineBuffer, "obj[%d]%s+=%d\n", param1, getObjPramName(param2), param3);
- } else if (opcode - 1 == 0x3) {
- sprintf(lineBuffer, "obj[%d]%s-=%d\n", param1, getObjPramName(param2), param3);
- } else if (opcode - 1 == 0x4) {
- sprintf(lineBuffer, "obj[%d]%s+=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2));
- } else if (opcode - 1 == 0x5) {
- sprintf(lineBuffer, "obj[%d]%s-=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2));
- } else if (opcode - 1 == 0x6) {
- sprintf(compareString1, "obj[%d]%s", param1, getObjPramName(param2));
- sprintf(compareString2, "%d", param3);
- }
- break;
+ case 0x6: {
+ byte param1;
+ byte param2;
+ int16 param3;
+
+ param1 = *(localScriptPtr + position);
+ position++;
+
+ param2 = *(localScriptPtr + position);
+ position++;
+
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
+
+ if (opcode - 1 == 0x2) {
+ sprintf(lineBuffer, "obj[%d]%s+=%d\n", param1, getObjPramName(param2), param3);
+ } else if (opcode - 1 == 0x3) {
+ sprintf(lineBuffer, "obj[%d]%s-=%d\n", param1, getObjPramName(param2), param3);
+ } else if (opcode - 1 == 0x4) {
+ sprintf(lineBuffer, "obj[%d]%s+=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2));
+ } else if (opcode - 1 == 0x5) {
+ sprintf(lineBuffer, "obj[%d]%s-=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2));
+ } else if (opcode - 1 == 0x6) {
+ sprintf(compareString1, "obj[%d]%s", param1, getObjPramName(param2));
+ sprintf(compareString2, "%d", param3);
}
+ break;
+ }
case 0x7:
- case 0x8:
- {
- byte param1;
- int16 param2;
- int16 param3;
- int16 param4;
- int16 param5;
+ case 0x8: {
+ byte param1;
+ int16 param2;
+ int16 param3;
+ int16 param4;
+ int16 param5;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param2 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param4 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param4 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param5 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param5 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- if (opcode - 1 == 0x7) {
- sprintf(lineBuffer, "setupObject(Idx:%d,X:%d,Y:%d,mask:%d,frame:%d)\n", param1, param2, param3, param4, param5);
- } else if (opcode - 1 == 0x8) {
- sprintf(lineBuffer, "checkCollision(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
- }
- break;
+ if (opcode - 1 == 0x7) {
+ sprintf(lineBuffer, "setupObject(Idx:%d,X:%d,Y:%d,mask:%d,frame:%d)\n", param1, param2, param3, param4, param5);
+ } else if (opcode - 1 == 0x8) {
+ sprintf(lineBuffer, "checkCollision(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
}
- case 0x9:
- {
- byte param1;
- int16 param2;
+ break;
+ }
+ case 0x9: {
+ byte param1;
+ int16 param2;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- if (param2) {
- byte param3;
-
- param3 = *(localScriptPtr + position);
- position++;
-
- if (param2 == 1) {
- sprintf(lineBuffer, "var[%d]=var[%d]\n", param1, param3);
- } else if (param2 == 2) {
- sprintf(lineBuffer, "var[%d]=globalVar[%d]\n", param1, param3);
- } else if (param2 == 3) {
- sprintf(lineBuffer, "var[%d]=mouse.X\n", param1);
- } else if (param2 == 4) {
- sprintf(lineBuffer, "var[%d]=mouse.Y\n", param1);
- } else if (param2 == 5) {
- sprintf(lineBuffer, "var[%d]=rand() mod %d\n", param1, param3);
- } else if (param2 == 8) {
- sprintf(lineBuffer, "var[%d]=file[%d].packedSize\n", param1, param3);
- } else if (param2 == 9) {
- sprintf(lineBuffer, "var[%d]=file[%d].unpackedSize\n", param1, param3);
- } else {
- error("decompileScript: 0x09: param2 = %d", param2);
- }
- } else {
- int16 param3;
+ if (param2) {
+ byte param3;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "var[%d]=%d\n", param1, param3);
+ if (param2 == 1) {
+ sprintf(lineBuffer, "var[%d]=var[%d]\n", param1, param3);
+ } else if (param2 == 2) {
+ sprintf(lineBuffer, "var[%d]=globalVar[%d]\n", param1, param3);
+ } else if (param2 == 3) {
+ sprintf(lineBuffer, "var[%d]=mouse.X\n", param1);
+ } else if (param2 == 4) {
+ sprintf(lineBuffer, "var[%d]=mouse.Y\n", param1);
+ } else if (param2 == 5) {
+ sprintf(lineBuffer, "var[%d]=rand() mod %d\n", param1, param3);
+ } else if (param2 == 8) {
+ sprintf(lineBuffer, "var[%d]=file[%d].packedSize\n", param1, param3);
+ } else if (param2 == 9) {
+ sprintf(lineBuffer, "var[%d]=file[%d].unpackedSize\n", param1, param3);
+ } else {
+ error("decompileScript: 0x09: param2 = %d", param2);
}
+ } else {
+ int16 param3;
- break;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
+
+ sprintf(lineBuffer, "var[%d]=%d\n", param1, param3);
}
+
+ break;
+ }
case 0xA:
case 0xB:
case 0xC:
- case 0xD:
- {
- byte param1;
- byte param2;
+ case 0xD: {
+ byte param1;
+ byte param2;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
+ param2 = *(localScriptPtr + position);
+ position++;
+
+ if (param2) {
+ byte param3;
+
+ param3 = *(localScriptPtr + position);
position++;
- if (param2) {
- byte param3;
+ if (opcode - 1 == 0xA) {
+ sprintf(lineBuffer, "var[%d]+=var[%d]\n", param1, param3);
+ } else if (opcode - 1 == 0xB) {
+ sprintf(lineBuffer, "var[%d]-=var[%d]\n", param1, param3);
+ } else if (opcode - 1 == 0xC) {
+ sprintf(lineBuffer, "var[%d]*=var[%d]\n", param1, param3);
+ } else if (opcode - 1 == 0xD) {
+ sprintf(lineBuffer, "var[%d]/=var[%d]\n", param1, param3);
+ }
+ } else {
+ int16 param3;
- param3 = *(localScriptPtr + position);
- position++;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- if (opcode - 1 == 0xA) {
- sprintf(lineBuffer, "var[%d]+=var[%d]\n", param1, param3);
- } else if (opcode - 1 == 0xB) {
- sprintf(lineBuffer, "var[%d]-=var[%d]\n", param1, param3);
- } else if (opcode - 1 == 0xC) {
- sprintf(lineBuffer, "var[%d]*=var[%d]\n", param1, param3);
- } else if (opcode - 1 == 0xD) {
- sprintf(lineBuffer, "var[%d]/=var[%d]\n", param1, param3);
- }
- } else {
- int16 param3;
-
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
-
- if (opcode - 1 == 0xA) {
- sprintf(lineBuffer, "var[%d]+=%d\n", param1, param3);
- } else if (opcode - 1 == 0xB) {
- sprintf(lineBuffer, "var[%d]-=%d\n", param1, param3);
- } else if (opcode - 1 == 0xC) {
- sprintf(lineBuffer, "var[%d]*=%d\n", param1, param3);
- } else if (opcode - 1 == 0xD) {
- sprintf(lineBuffer, "var[%d]/=%d\n", param1, param3);
- }
+ if (opcode - 1 == 0xA) {
+ sprintf(lineBuffer, "var[%d]+=%d\n", param1, param3);
+ } else if (opcode - 1 == 0xB) {
+ sprintf(lineBuffer, "var[%d]-=%d\n", param1, param3);
+ } else if (opcode - 1 == 0xC) {
+ sprintf(lineBuffer, "var[%d]*=%d\n", param1, param3);
+ } else if (opcode - 1 == 0xD) {
+ sprintf(lineBuffer, "var[%d]/=%d\n", param1, param3);
}
- break;
}
- case 0xE:
- {
- byte param1;
- byte param2;
+ break;
+ }
+ case 0xE: {
+ byte param1;
+ byte param2;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- if (param2) {
- byte param3;
+ if (param2) {
+ byte param3;
- param3 = *(localScriptPtr + position);
- position++;
+ param3 = *(localScriptPtr + position);
+ position++;
- if (param2 == 1) {
- sprintf(compareString1, "var[%d]", param1);
- sprintf(compareString2, "var[%d]", param3);
+ if (param2 == 1) {
+ sprintf(compareString1, "var[%d]", param1);
+ sprintf(compareString2, "var[%d]", param3);
- } else if (param2 == 2) {
- sprintf(compareString1, "var[%d]", param1);
- sprintf(compareString2, "globalVar[%d]", param3);
- } else {
- error("decompileScript: 0x0E: param2 = %d", param2);
- }
+ } else if (param2 == 2) {
+ sprintf(compareString1, "var[%d]", param1);
+ sprintf(compareString2, "globalVar[%d]", param3);
} else {
- int16 param3;
+ error("decompileScript: 0x0E: param2 = %d", param2);
+ }
+ } else {
+ int16 param3;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- sprintf(compareString1, "var[%d]", param1);
- sprintf(compareString2, "%d", param3);
- }
- break;
+ sprintf(compareString1, "var[%d]", param1);
+ sprintf(compareString2, "%d", param3);
}
- case 0xF:
- {
- byte param1;
- byte param2;
- byte param3;
+ break;
+ }
+ case 0xF: {
+ byte param1;
+ byte param2;
+ byte param3;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- param3 = *(localScriptPtr + position);
- position++;
+ param3 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "obj[%d]%s=var[%d]\n", param1, getObjPramName(param2), param3);
+ sprintf(lineBuffer, "obj[%d]%s=var[%d]\n", param1, getObjPramName(param2), param3);
- break;
- }
+ break;
+ }
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
- case 0x19:
- {
- byte param;
-
- param = *(localScriptPtr + position);
- position++;
-
- if (opcode - 1 == 0x13) {
- sprintf(lineBuffer, "loadMask0(%d)\n", param);
- } else if (opcode - 1 == 0x14) {
- sprintf(lineBuffer, "unloadMask0(%d)\n", param);
- } else if (opcode - 1 == 0x15) {
- sprintf(lineBuffer, "OP_15(%d)\n", param);
- } else if (opcode - 1 == 0x16) {
- sprintf(lineBuffer, "loadMask1(%d)\n", param);
- } else if (opcode - 1 == 0x17) {
- sprintf(lineBuffer, "unloadMask0(%d)\n", param);
- } else if (opcode - 1 == 0x18) {
- sprintf(lineBuffer, "loadMask4(%d)\n", param);
- } else if (opcode - 1 == 0x19) {
- sprintf(lineBuffer, "unloadMask4(%d)\n", param);
- }
- break;
+ case 0x19: {
+ byte param;
+
+ param = *(localScriptPtr + position);
+ position++;
+
+ if (opcode - 1 == 0x13) {
+ sprintf(lineBuffer, "loadMask0(%d)\n", param);
+ } else if (opcode - 1 == 0x14) {
+ sprintf(lineBuffer, "unloadMask0(%d)\n", param);
+ } else if (opcode - 1 == 0x15) {
+ sprintf(lineBuffer, "OP_15(%d)\n", param);
+ } else if (opcode - 1 == 0x16) {
+ sprintf(lineBuffer, "loadMask1(%d)\n", param);
+ } else if (opcode - 1 == 0x17) {
+ sprintf(lineBuffer, "unloadMask0(%d)\n", param);
+ } else if (opcode - 1 == 0x18) {
+ sprintf(lineBuffer, "loadMask4(%d)\n", param);
+ } else if (opcode - 1 == 0x19) {
+ sprintf(lineBuffer, "unloadMask4(%d)\n", param);
}
- case 0x1A:
- {
- byte param;
+ break;
+ }
+ case 0x1A: {
+ byte param;
- param = *(localScriptPtr + position);
- position++;
+ param = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "OP_1A(%d)\n", param);
+ sprintf(lineBuffer, "OP_1A(%d)\n", param);
- break;
- }
- case 0x1B:
- {
- sprintf(lineBuffer, "bgIncrustList.clear()\n");
- break;
- }
- case 0x1D:
- {
- byte param;
+ break;
+ }
+ case 0x1B: {
+ sprintf(lineBuffer, "bgIncrustList.clear()\n");
+ break;
+ }
+ case 0x1D: {
+ byte param;
- param = *(localScriptPtr + position);
- position++;
+ param = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "label(%d)\n", param);
+ sprintf(lineBuffer, "label(%d)\n", param);
- break;
- }
- case 0x1E:
- {
- byte param;
+ break;
+ }
+ case 0x1E: {
+ byte param;
- param = *(localScriptPtr + position);
- position++;
+ param = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "goto(%d)\n", param);
+ sprintf(lineBuffer, "goto(%d)\n", param);
- break;
- }
+ break;
+ }
// If cases
case 0x1F:
case 0x20:
case 0x21:
case 0x22:
case 0x23:
- case 0x24:
- {
- byte param;
-
- param = *(localScriptPtr + position);
- position++;
-
- if (opcode - 1 == 0x1F) {
- sprintf(lineBuffer, "if(%s>%s) goto(%d)\n", compareString1, compareString2, param);
- } else if (opcode - 1 == 0x20) {
- sprintf(lineBuffer, "if(%s>=%s) goto(%d)\n", compareString1, compareString2, param);
- } else if (opcode - 1 == 0x21) {
- sprintf(lineBuffer, "if(%s<%s) goto(%d)\n", compareString1, compareString2, param);
- } else if (opcode - 1 == 0x22) {
- sprintf(lineBuffer, "if(%s<=%s) goto(%d)\n", compareString1, compareString2, param);
- } else if (opcode - 1 == 0x23) {
- sprintf(lineBuffer, "if(%s==%s) goto(%d)\n", compareString1, compareString2, param);
- } else if (opcode - 1 == 0x24) {
- sprintf(lineBuffer, "if(%s!=%s) goto(%d)\n", compareString1, compareString2, param);
- }
- break;
+ case 0x24: {
+ byte param;
+
+ param = *(localScriptPtr + position);
+ position++;
+
+ if (opcode - 1 == 0x1F) {
+ sprintf(lineBuffer, "if(%s>%s) goto(%d)\n", compareString1, compareString2, param);
+ } else if (opcode - 1 == 0x20) {
+ sprintf(lineBuffer, "if(%s>=%s) goto(%d)\n", compareString1, compareString2, param);
+ } else if (opcode - 1 == 0x21) {
+ sprintf(lineBuffer, "if(%s<%s) goto(%d)\n", compareString1, compareString2, param);
+ } else if (opcode - 1 == 0x22) {
+ sprintf(lineBuffer, "if(%s<=%s) goto(%d)\n", compareString1, compareString2, param);
+ } else if (opcode - 1 == 0x23) {
+ sprintf(lineBuffer, "if(%s==%s) goto(%d)\n", compareString1, compareString2, param);
+ } else if (opcode - 1 == 0x24) {
+ sprintf(lineBuffer, "if(%s!=%s) goto(%d)\n", compareString1, compareString2, param);
}
- case 0x25:
- {
- byte param;
+ break;
+ }
+ case 0x25: {
+ byte param;
- param = *(localScriptPtr + position);
- position++;
+ param = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "removeLabel(%d)\n", param);
+ sprintf(lineBuffer, "removeLabel(%d)\n", param);
- break;
- }
- case 0x26:
- {
- byte param1;
- byte param2;
+ break;
+ }
+ case 0x26: {
+ byte param1;
+ byte param2;
- param1 = *(localScriptPtr + position);
- position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "loop(--var[%d]) -> label(%d)\n", param1, param2);
+ sprintf(lineBuffer, "loop(--var[%d]) -> label(%d)\n", param1, param2);
- break;
- }
+ break;
+ }
case 0x31:
- case 0x32:
- {
- byte param;
+ case 0x32: {
+ byte param;
- param = *(localScriptPtr + position);
- position++;
+ param = *(localScriptPtr + position);
+ position++;
- if (opcode - 1 == 0x31) {
- sprintf(lineBuffer, "startGlobalScript(%d)\n", param);
- } else if (opcode - 1 == 0x32) {
- sprintf(lineBuffer, "endGlobalScript(%d)\n", param);
- }
- break;
+ if (opcode - 1 == 0x31) {
+ sprintf(lineBuffer, "startGlobalScript(%d)\n", param);
+ } else if (opcode - 1 == 0x32) {
+ sprintf(lineBuffer, "endGlobalScript(%d)\n", param);
}
+ break;
+ }
case 0x3B:
case 0x3C:
case 0x3D:
- case OP_loadPart:
- {
- if (opcode - 1 == 0x3B) {
- sprintf(lineBuffer, "loadResource(%s)\n", localScriptPtr + position);
- } else if (opcode - 1 == 0x3C) {
- sprintf(lineBuffer, "loadBg(%s)\n", localScriptPtr + position);
- } else if (opcode - 1 == 0x3D) {
- sprintf(lineBuffer, "loadCt(%s)\n", localScriptPtr + position);
- } else if (opcode - 1 == OP_loadPart) {
- sprintf(lineBuffer, "loadPart(%s)\n", localScriptPtr + position);
- }
-
- position += strlen((const char *)localScriptPtr + position) + 1;
- break;
- }
- case 0x40:
- {
- sprintf(lineBuffer, "closePart()\n");
- break;
- }
- case OP_loadNewPrcName:
- {
- byte param;
+ case OP_loadPart: {
+ if (opcode - 1 == 0x3B) {
+ sprintf(lineBuffer, "loadResource(%s)\n", localScriptPtr + position);
+ } else if (opcode - 1 == 0x3C) {
+ sprintf(lineBuffer, "loadBg(%s)\n", localScriptPtr + position);
+ } else if (opcode - 1 == 0x3D) {
+ sprintf(lineBuffer, "loadCt(%s)\n", localScriptPtr + position);
+ } else if (opcode - 1 == OP_loadPart) {
+ sprintf(lineBuffer, "loadPart(%s)\n", localScriptPtr + position);
+ }
+
+ position += strlen((const char *)localScriptPtr + position) + 1;
+ break;
+ }
+ case 0x40: {
+ sprintf(lineBuffer, "closePart()\n");
+ break;
+ }
+ case OP_loadNewPrcName: {
+ byte param;
- param = *(localScriptPtr + position);
- position++;
+ param = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "loadPrc(%d,%s)\n", param, localScriptPtr + position);
+ sprintf(lineBuffer, "loadPrc(%d,%s)\n", param, localScriptPtr + position);
- position += strlen((const char *)localScriptPtr + position) + 1;
- break;
- }
- case OP_requestCheckPendingDataLoad: // nop
- {
- sprintf(lineBuffer, "requestCheckPendingDataLoad()\n");
- break;
- }
- case 0x45:
- {
- sprintf(lineBuffer, "blitAndFade()\n");
- break;
- }
- case 0x46:
- {
- sprintf(lineBuffer, "fadeToBlack()\n");
- break;
- }
- case 0x47:
- {
- byte param1;
- byte param2;
- int16 param3;
- int16 param4;
- int16 param5;
+ position += strlen((const char *)localScriptPtr + position) + 1;
+ break;
+ }
+ case OP_requestCheckPendingDataLoad: { // nop
+ sprintf(lineBuffer, "requestCheckPendingDataLoad()\n");
+ break;
+ }
+ case 0x45: {
+ sprintf(lineBuffer, "blitAndFade()\n");
+ break;
+ }
+ case 0x46: {
+ sprintf(lineBuffer, "fadeToBlack()\n");
+ break;
+ }
+ case 0x47: {
+ byte param1;
+ byte param2;
+ int16 param3;
+ int16 param4;
+ int16 param5;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param4 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param4 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param5 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param5 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- sprintf(lineBuffer, "transformPaletteRange(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
+ sprintf(lineBuffer, "transformPaletteRange(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
- break;
- }
- case 0x49:
- {
- byte param;
+ break;
+ }
+ case 0x49: {
+ byte param;
- param = *(localScriptPtr + position);
- position++;
+ param = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "setDefaultMenuBgColor(%d)\n", param);
+ sprintf(lineBuffer, "setDefaultMenuBgColor(%d)\n", param);
- break;
- }
- case 0x4F:
- {
- sprintf(lineBuffer, "break()\n");
- exitScript = 1;
- break;
- }
- case 0x50:
- {
- sprintf(lineBuffer, "endScript()\n\n");
- break;
- }
- case 0x51:
- {
- byte param1;
- int16 param2;
- int16 param3;
- int16 param4;
- int16 param5;
+ break;
+ }
+ case 0x4F: {
+ sprintf(lineBuffer, "break()\n");
+ exitScript = 1;
+ break;
+ }
+ case 0x50: {
+ sprintf(lineBuffer, "endScript()\n\n");
+ break;
+ }
+ case 0x51: {
+ byte param1;
+ int16 param2;
+ int16 param3;
+ int16 param4;
+ int16 param5;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param2 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param4 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param4 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param5 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param5 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- sprintf(lineBuffer, "message(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
+ sprintf(lineBuffer, "message(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
- break;
- }
+ break;
+ }
case 0x52:
- case 0x53:
- {
- byte param1;
- byte param2;
+ case 0x53: {
+ byte param1;
+ byte param2;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- if (param2) {
- byte param3;
-
- param3 = *(localScriptPtr + position);
- position++;
-
- if (param2 == 1) {
- if (opcode - 1 == 0x52) {
- sprintf(lineBuffer, "globalVar[%d] = var[%d]\n", param1, param3);
- } else if (opcode - 1 == 0x53) {
- sprintf(compareString1, "globalVar[%d]", param1);
- sprintf(compareString2, "var[%d]", param3);
- }
- } else if (param2 == 2) {
- if (opcode - 1 == 0x52) {
- sprintf(lineBuffer, "globalVar[%d] = globalVar[%d]\n", param1, param3);
- } else if (opcode - 1 == 0x53) {
- sprintf(compareString1, "globalVar[%d]", param1);
- sprintf(compareString2, "globalVar[%d]", param3);
- }
- } else {
- if (opcode - 1 == 0x52) {
- error("decompileScript: 0x52: param2 = %d", param2);
- } else if (opcode - 1 == 0x53) {
- error("decompileScript: 0x53: param2 = %d", param2);
- }
- }
- } else {
- int16 param3;
+ if (param2) {
+ byte param3;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = *(localScriptPtr + position);
+ position++;
+ if (param2 == 1) {
if (opcode - 1 == 0x52) {
- sprintf(lineBuffer, "globalVar[%d] = %d\n", param1, param3);
+ sprintf(lineBuffer, "globalVar[%d] = var[%d]\n", param1, param3);
} else if (opcode - 1 == 0x53) {
sprintf(compareString1, "globalVar[%d]", param1);
- sprintf(compareString2, "%d", param3);
+ sprintf(compareString2, "var[%d]", param3);
+ }
+ } else if (param2 == 2) {
+ if (opcode - 1 == 0x52) {
+ sprintf(lineBuffer, "globalVar[%d] = globalVar[%d]\n", param1, param3);
+ } else if (opcode - 1 == 0x53) {
+ sprintf(compareString1, "globalVar[%d]", param1);
+ sprintf(compareString2, "globalVar[%d]", param3);
+ }
+ } else {
+ if (opcode - 1 == 0x52) {
+ error("decompileScript: 0x52: param2 = %d", param2);
+ } else if (opcode - 1 == 0x53) {
+ error("decompileScript: 0x53: param2 = %d", param2);
}
}
- break;
- }
- case 0x59:
- {
- sprintf(lineBuffer, "comment: %s\n", localScriptPtr + position);
+ } else {
+ int16 param3;
- position += strlen((const char *)localScriptPtr + position);
- break;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
+
+ if (opcode - 1 == 0x52) {
+ sprintf(lineBuffer, "globalVar[%d] = %d\n", param1, param3);
+ } else if (opcode - 1 == 0x53) {
+ sprintf(compareString1, "globalVar[%d]", param1);
+ sprintf(compareString2, "%d", param3);
+ }
}
- case 0x5A:
- {
- byte param1;
- byte param2;
+ break;
+ }
+ case 0x59: {
+ sprintf(lineBuffer, "comment: %s\n", localScriptPtr + position);
- param1 = *(localScriptPtr + position);
- position++;
+ position += strlen((const char *)localScriptPtr + position);
+ break;
+ }
+ case 0x5A: {
+ byte param1;
+ byte param2;
- param2 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "freePartRang(%d,%d)\n", param1, param2);
+ param2 = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x5B:
- {
- sprintf(lineBuffer, "unloadAllMasks()\n");
- break;
- }
- case 0x65:
- {
- sprintf(lineBuffer, "setupTableUnk1()\n");
- break;
- }
- case 0x66:
- {
- byte param1;
- int16 param2;
+ sprintf(lineBuffer, "freePartRang(%d,%d)\n", param1, param2);
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x5B: {
+ sprintf(lineBuffer, "unloadAllMasks()\n");
+ break;
+ }
+ case 0x65: {
+ sprintf(lineBuffer, "setupTableUnk1()\n");
+ break;
+ }
+ case 0x66: {
+ byte param1;
+ int16 param2;
- param2 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param1 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "tableUnk1[%d] = %d\n", param1, param2);
+ param2 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- break;
- }
- case 0x68:
- {
- byte param;
+ sprintf(lineBuffer, "tableUnk1[%d] = %d\n", param1, param2);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x68: {
+ byte param;
- sprintf(lineBuffer, "setPlayerCommandPosY(%d)\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x69:
- {
- sprintf(lineBuffer, "allowPlayerInput()\n");
- break;
- }
- case 0x6A:
- {
- sprintf(lineBuffer, "disallowPlayerInput()\n");
- break;
- }
- case 0x6B:
- {
- byte newDisk;
+ sprintf(lineBuffer, "setPlayerCommandPosY(%d)\n", param);
- newDisk = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x69: {
+ sprintf(lineBuffer, "allowPlayerInput()\n");
+ break;
+ }
+ case 0x6A: {
+ sprintf(lineBuffer, "disallowPlayerInput()\n");
+ break;
+ }
+ case 0x6B: {
+ byte newDisk;
- sprintf(lineBuffer, "changeDataDisk(%d)\n", newDisk);
+ newDisk = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x6D:
- {
- sprintf(lineBuffer, "loadDat(%s)\n", localScriptPtr + position);
+ sprintf(lineBuffer, "changeDataDisk(%d)\n", newDisk);
- position += strlen((const char *)localScriptPtr + position) + 1;
- break;
- }
- case 0x6E: // nop
- {
- sprintf(lineBuffer, "updateDat()\n");
- break;
- }
- case 0x6F:
- {
- sprintf(lineBuffer, "OP_6F() -> dat related\n");
- break;
- }
- case 0x70:
- {
- sprintf(lineBuffer, "stopSample()\n");
- break;
- }
- case 0x79:
- {
- byte param;
+ break;
+ }
+ case 0x6D: {
+ sprintf(lineBuffer, "loadDat(%s)\n", localScriptPtr + position);
- param = *(localScriptPtr + position);
- position++;
+ position += strlen((const char *)localScriptPtr + position) + 1;
+ break;
+ }
+ case 0x6E: { // nop
+ sprintf(lineBuffer, "updateDat()\n");
+ break;
+ }
+ case 0x6F: {
+ sprintf(lineBuffer, "OP_6F() -> dat related\n");
+ break;
+ }
+ case 0x70: {
+ sprintf(lineBuffer, "stopSample()\n");
+ break;
+ }
+ case 0x79: {
+ byte param;
- sprintf(lineBuffer, "disableSystemMenu(%d)\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
+ sprintf(lineBuffer, "disableSystemMenu(%d)\n", param);
+
+ break;
+ }
case 0x77:
- case 0x78:
- {
- byte param1;
- byte param2;
- int16 param3;
- byte param4;
- int16 param5;
- int16 param6;
+ case 0x78: {
+ byte param1;
+ byte param2;
+ int16 param3;
+ byte param4;
+ int16 param5;
+ int16 param6;
- param1 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param4 = *(localScriptPtr + position);
- position++;
+ param4 = *(localScriptPtr + position);
+ position++;
- param5 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param5 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param6 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
-
- if (opcode - 1 == 0x77) {
- sprintf(lineBuffer, "playSample(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6);
- } else if (opcode - 1 == 0x78) {
- sprintf(lineBuffer, "OP_78(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6);
- }
+ param6 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- break;
+ if (opcode - 1 == 0x77) {
+ sprintf(lineBuffer, "playSample(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6);
+ } else if (opcode - 1 == 0x78) {
+ sprintf(lineBuffer, "playSampleSwapped(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6);
}
- case 0x7A:
- {
- byte param;
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x7A: {
+ byte param;
- sprintf(lineBuffer, "OP_7A(%d)\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x7B: // OS only
- {
- byte param;
+ sprintf(lineBuffer, "OP_7A(%d)\n", param);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x7B: { // OS only
+ byte param;
- sprintf(lineBuffer, "OP_7B(%d)\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x7F: // OS only
- {
- byte param1;
- byte param2;
- byte param3;
- byte param4;
- int16 param5;
- int16 param6;
- int16 param7;
+ sprintf(lineBuffer, "OP_7B(%d)\n", param);
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x7F: { // OS only
+ byte param1;
+ byte param2;
+ byte param3;
+ byte param4;
+ int16 param5;
+ int16 param6;
+ int16 param7;
- param2 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param3 = *(localScriptPtr + position);
- position++;
+ param2 = *(localScriptPtr + position);
+ position++;
- param4 = *(localScriptPtr + position);
- position++;
+ param3 = *(localScriptPtr + position);
+ position++;
- param5 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param4 = *(localScriptPtr + position);
+ position++;
- param6 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param5 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param7 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param6 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- sprintf(lineBuffer, "OP_7F(%d,%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6, param7);
+ param7 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- break;
- }
- case 0x80: // OS only
- {
- byte param1;
- byte param2;
+ sprintf(lineBuffer, "OP_7F(%d,%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6, param7);
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x80: { // OS only
+ byte param1;
+ byte param2;
- param2 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "OP_80(%d,%d)\n", param1, param2);
+ param2 = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x82: // OS only
- {
- byte param1;
- byte param2;
- uint16 param3;
- uint16 param4;
- byte param5;
+ sprintf(lineBuffer, "OP_80(%d,%d)\n", param1, param2);
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x82: { // OS only
+ byte param1;
+ byte param2;
+ uint16 param3;
+ uint16 param4;
+ byte param5;
- param2 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param2 = *(localScriptPtr + position);
+ position++;
- param4 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param5 = *(localScriptPtr + position);
- position++;
+ param4 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- sprintf(lineBuffer, "OP_82(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
+ param5 = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x83: // OS only
- {
- byte param1;
- byte param2;
+ sprintf(lineBuffer, "OP_82(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x83: { // OS only
+ byte param1;
+ byte param2;
- param2 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "OP_83(%d,%d)\n", param1, param2);
+ param2 = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x89: // OS only
- {
- byte param;
+ sprintf(lineBuffer, "OP_83(%d,%d)\n", param1, param2);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x89: { // OS only
+ byte param;
- sprintf(lineBuffer, "if(%s!=%s) goto next label(%d)\n", compareString1, compareString2, param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x8B: // OS only
- {
- byte param;
+ sprintf(lineBuffer, "if(%s!=%s) goto next label(%d)\n", compareString1, compareString2, param);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x8B: { // OS only
+ byte param;
- sprintf(lineBuffer, "OP_8B(%d)\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x8C: // OS only
- {
- byte param;
+ sprintf(lineBuffer, "OP_8B(%d)\n", param);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x8C: { // OS only
+ byte param;
- sprintf(lineBuffer, "OP_8C(%d)\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x8D: // OS only
- {
- int16 param1;
- int16 param2;
- int16 param3;
- int16 param4;
- int16 param5;
- int16 param6;
- int16 param7;
- int16 param8;
+ sprintf(lineBuffer, "OP_8C(%d)\n", param);
- param1 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ break;
+ }
+ case 0x8D: { // OS only
+ int16 param1;
+ int16 param2;
+ int16 param3;
+ int16 param4;
+ int16 param5;
+ int16 param6;
+ int16 param7;
+ int16 param8;
- param2 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param1 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param3 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param2 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param4 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param3 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param5 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param4 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param6 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param5 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param7 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param6 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- param8 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ param7 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- sprintf(compareString1, "obj[%d]", param1);
- sprintf(compareString2, "{%d,%d,%d,%d,%d,%d,%d}", param2, param3, param4, param5, param6, param7, param8);
+ param8 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- break;
- }
- case 0x8E: // OS only
- {
- byte param1;
+ sprintf(compareString1, "obj[%d]", param1);
+ sprintf(compareString2, "{%d,%d,%d,%d,%d,%d,%d}", param2, param3, param4, param5, param6, param7, param8);
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x8E: { // OS only
+ byte param1;
- sprintf(lineBuffer, "ADDBG(%d,%s)\n", param1, localScriptPtr + position);
+ param1 = *(localScriptPtr + position);
+ position++;
- position += strlen((const char *)localScriptPtr + position);
+ sprintf(lineBuffer, "ADDBG(%d,%s)\n", param1, localScriptPtr + position);
- break;
- }
- case 0x8F: // OS only
- {
- byte param;
+ position += strlen((const char *)localScriptPtr + position);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x8F: { // OS only
+ byte param;
- sprintf(lineBuffer, "OP_8F(%d)\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x90: // OS only
- {
- byte param1;
+ sprintf(lineBuffer, "OP_8F(%d)\n", param);
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x90: { // OS only
+ byte param1;
- sprintf(lineBuffer, "loadABS(%d,%s)\n", param1, localScriptPtr + position);
+ param1 = *(localScriptPtr + position);
+ position++;
- position += strlen((const char *)localScriptPtr + position);
+ sprintf(lineBuffer, "loadABS(%d,%s)\n", param1, localScriptPtr + position);
- break;
- }
- case 0x91: // OS only
- {
- byte param;
+ position += strlen((const char *)localScriptPtr + position);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x91: { // OS only
+ byte param;
- sprintf(lineBuffer, "OP_91(%d)\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x9D: // OS only
- {
- byte param;
+ sprintf(lineBuffer, "OP_91(%d)\n", param);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x9D: { // OS only
+ byte param;
- sprintf(lineBuffer, "OP_9D(%d) -> flip img idx\n", param);
+ param = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0x9E: // OS only
- {
- byte param;
+ sprintf(lineBuffer, "OP_9D(%d) -> flip img idx\n", param);
- param = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0x9E: { // OS only
+ byte param;
- if (param) {
- byte param2;
+ param = *(localScriptPtr + position);
+ position++;
- param2 = *(localScriptPtr + position);
- position++;
+ if (param) {
+ byte param2;
- sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2);
- } else {
- int16 param2;
+ param2 = *(localScriptPtr + position);
+ position++;
- param2 = READ_BE_UINT16(localScriptPtr + position);
- position += 2;
+ sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2);
+ } else {
+ int16 param2;
- sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2);
- }
+ param2 = READ_BE_UINT16(localScriptPtr + position);
+ position += 2;
- break;
+ sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2);
}
- case 0xA0: // OS only
- {
- byte param1;
- byte param2;
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0xA0: { // OS only
+ byte param1;
+ byte param2;
- param2 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "OP_A0(%d,%d)\n", param1, param2);
+ param2 = *(localScriptPtr + position);
+ position++;
- break;
- }
- case 0xA1: // OS only
- {
- byte param1;
- byte param2;
+ sprintf(lineBuffer, "OP_A0(%d,%d)\n", param1, param2);
- param1 = *(localScriptPtr + position);
- position++;
+ break;
+ }
+ case 0xA1: { // OS only
+ byte param1;
+ byte param2;
- param2 = *(localScriptPtr + position);
- position++;
+ param1 = *(localScriptPtr + position);
+ position++;
- sprintf(lineBuffer, "OP_A1(%d,%d)\n", param1, param2);
+ param2 = *(localScriptPtr + position);
+ position++;
- break;
- }
- default:
- {
- sprintf(lineBuffer, "Unsupported opcode %X in decompileScript\n\n", opcode - 1);
- position = scriptSize;
- break;
- }
+ sprintf(lineBuffer, "OP_A1(%d,%d)\n", param1, param2);
+
+ break;
+ }
+ default: {
+ sprintf(lineBuffer, "Unsupported opcode %X in decompileScript\n\n", opcode - 1);
+ position = scriptSize;
+ break;
+ }
}
- // printf(lineBuffer);
+ //printf(lineBuffer);
strcpy(decompileBuffer[decompileBufferPosition++], lineBuffer);
exitScript = 0;
diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index 52e1cdac7e..10404ae56b 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -153,7 +153,7 @@ const int AdLibSoundDriver::_freqTable[] = {
const int AdLibSoundDriver::_freqTableCount = ARRAYSIZE(_freqTable);
const int AdLibSoundDriver::_operatorsTable[] = {
- 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21
+ 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21
};
const int AdLibSoundDriver::_operatorsTableCount = ARRAYSIZE(_operatorsTable);
@@ -614,7 +614,7 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in
}
MidiSoundDriverH32::MidiSoundDriverH32(MidiDriver *output)
- : _output(output), _callback(0), _mutex() {
+ : _output(output), _callback(0), _mutex() {
}
MidiSoundDriverH32::~MidiSoundDriverH32() {
@@ -731,13 +731,13 @@ void MidiSoundDriverH32::selectInstrument(int channel, int timbreGroup, int timb
0x00, 0x00, 0x00, // offset
0x00, // Timbre group _ timbreGroup * 64 + timbreNumber should be the
0x00, // Timbre number / MT-32 instrument in case timbreGroup is 0 or 1.
- 0x18, // Key shift (= 0)
+ 0x18, // Key shift (= 0)
0x32, // Fine tune (= 0)
0x0C, // Bender Range
0x03, // Assign Mode
0x01, // Reverb Switch (= enabled)
0x00, // dummy
- 0x00, // Output level
+ 0x00, // Output level
0x07, // Panpot (= balanced)
0x00, // dummy
0x00, // dummy
@@ -998,19 +998,55 @@ void PCSound::stopSound(int channel) {
}
PaulaSound::PaulaSound(Audio::Mixer *mixer, CineEngine *vm)
- : Sound(mixer, vm) {
+ : Sound(mixer, vm), _sfxTimer(0), _musicTimer(0), _musicFadeTimer(0) {
_moduleStream = 0;
+ // The original is using the following timer frequency:
+ // 0.709379Mhz / 8000 = 88.672375Hz
+ // 1000000 / 88.672375Hz = 11277.46944863us
+ g_system->getTimerManager()->installTimerProc(&PaulaSound::sfxTimerProc, 11277, this, "PaulaSound::sfxTimerProc");
+ // The original is using the following timer frequency:
+ // 0.709379Mhz / 14565 = 48.704359Hz
+ // 1000000 / 48.704359Hz = 20532.04313806us
+ g_system->getTimerManager()->installTimerProc(&PaulaSound::musicTimerProc, 20532, this, "PaulaSound::musicTimerProc");
}
PaulaSound::~PaulaSound() {
+ Common::StackLock sfxLock(_sfxMutex);
+ g_system->getTimerManager()->removeTimerProc(&PaulaSound::sfxTimerProc);
for (int i = 0; i < NUM_CHANNELS; ++i) {
stopSound(i);
}
+
+ Common::StackLock musicLock(_musicMutex);
+ g_system->getTimerManager()->removeTimerProc(&PaulaSound::musicTimerProc);
stopMusic();
}
void PaulaSound::loadMusic(const char *name) {
debugC(5, kCineDebugSound, "PaulaSound::loadMusic('%s')", name);
+ for (int i = 0; i < NUM_CHANNELS; ++i) {
+ stopSound(i);
+ }
+
+ // Fade music out when there is music playing.
+ _musicMutex.lock();
+ if (_mixer->isSoundHandleActive(_moduleHandle)) {
+ // Only start fade out when it is not in progress.
+ if (!_musicFadeTimer) {
+ _musicFadeTimer = 1;
+ }
+
+ _musicMutex.unlock();
+ while (_musicFadeTimer != 64) {
+ g_system->delayMillis(50);
+ }
+ } else {
+ _musicMutex.unlock();
+ }
+
+ Common::StackLock lock(_musicMutex);
+ assert(!_mixer->isSoundHandleActive(_moduleHandle));
+
if (_vm->getGameType() == GType_FW) {
// look for separate files
Common::File f;
@@ -1031,54 +1067,135 @@ void PaulaSound::loadMusic(const char *name) {
void PaulaSound::playMusic() {
debugC(5, kCineDebugSound, "PaulaSound::playMusic()");
+ Common::StackLock lock(_musicMutex);
+
_mixer->stopHandle(_moduleHandle);
if (_moduleStream) {
+ _musicFadeTimer = 0;
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_moduleHandle, _moduleStream);
}
}
void PaulaSound::stopMusic() {
debugC(5, kCineDebugSound, "PaulaSound::stopMusic()");
+ Common::StackLock lock(_musicMutex);
+
_mixer->stopHandle(_moduleHandle);
}
void PaulaSound::fadeOutMusic() {
debugC(5, kCineDebugSound, "PaulaSound::fadeOutMusic()");
- // TODO
- stopMusic();
+ Common::StackLock lock(_musicMutex);
+
+ _musicFadeTimer = 1;
}
void PaulaSound::playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) {
- // TODO: handle volume slides and repeat
debugC(5, kCineDebugSound, "PaulaSound::playSound() channel %d size %d", channel, size);
+ Common::StackLock lock(_sfxMutex);
+ assert(frequency > 0);
+
stopSound(channel);
- size = MIN<int>(size - SPL_HDR_SIZE, READ_BE_UINT16(data + 4));
- // TODO: consider skipping the header in loadSpl directly
if (size > 0) {
byte *sound = (byte *)malloc(size);
if (sound) {
- memcpy(sound, data + SPL_HDR_SIZE, size);
- playSoundChannel(channel, frequency, sound, size, volume);
+ // Create the audio stream
+ memcpy(sound, data, size);
+
+ // Clear the first and last 16 bits like in the original.
+ sound[0] = sound[1] = sound[size - 2] = sound[size - 1] = 0;
+
+ Audio::SeekableAudioStream *stream = Audio::makeRawStream(sound, size, PAULA_FREQ / frequency, 0);
+
+ // Initialize the volume control
+ _channelsTable[channel].initialize(volume, volumeStep, stepCount);
+
+ // Start the sfx
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channelsTable[channel].handle,
+ Audio::makeLoopingAudioStream(stream, repeat ? 0 : 1),
+ -1, volume * Audio::Mixer::kMaxChannelVolume / 63,
+ _channelBalance[channel]);
}
}
}
void PaulaSound::stopSound(int channel) {
debugC(5, kCineDebugSound, "PaulaSound::stopSound() channel %d", channel);
- _mixer->stopHandle(_channelsTable[channel]);
+ Common::StackLock lock(_sfxMutex);
+
+ _mixer->stopHandle(_channelsTable[channel].handle);
}
-void PaulaSound::update() {
- // process volume slides and start sound playback
- // TODO
+void PaulaSound::sfxTimerProc(void *param) {
+ PaulaSound *sound = (PaulaSound *)param;
+ sound->sfxTimerCallback();
}
-void PaulaSound::playSoundChannel(int channel, int frequency, uint8 *data, int size, int volume) {
- assert(frequency > 0);
- frequency = PAULA_FREQ / frequency;
- Audio::AudioStream *stream = Audio::makeRawStream(data, size, frequency, 0);
- _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channelsTable[channel], stream);
- _mixer->setChannelVolume(_channelsTable[channel], volume * Audio::Mixer::kMaxChannelVolume / 63);
+void PaulaSound::sfxTimerCallback() {
+ Common::StackLock lock(_sfxMutex);
+
+ if (_sfxTimer < 6) {
+ ++_sfxTimer;
+
+ for (int i = 0; i < NUM_CHANNELS; ++i) {
+ // Only process active channels
+ if (!_mixer->isSoundHandleActive(_channelsTable[i].handle)) {
+ continue;
+ }
+
+ if (_channelsTable[i].curStep) {
+ --_channelsTable[i].curStep;
+ } else {
+ _channelsTable[i].curStep = _channelsTable[i].stepCount;
+ const int volume = CLIP(_channelsTable[i].volume + _channelsTable[i].volumeStep, 0, 63);
+ _channelsTable[i].volume = volume;
+ // Unlike the original we stop silent sounds
+ if (volume) {
+ _mixer->setChannelVolume(_channelsTable[i].handle, volume * Audio::Mixer::kMaxChannelVolume / 63);
+ } else {
+ _mixer->stopHandle(_channelsTable[i].handle);
+ }
+ }
+ }
+ } else {
+ _sfxTimer = 0;
+ // Possible TODO: The original only ever started sounds here. This
+ // should not be noticable though. So we do not do it for now.
+ }
}
+void PaulaSound::musicTimerProc(void *param) {
+ PaulaSound *sound = (PaulaSound *)param;
+ sound->musicTimerCallback();
+}
+
+void PaulaSound::musicTimerCallback() {
+ Common::StackLock lock(_musicMutex);
+
+ ++_musicTimer;
+ if (_musicTimer == 6) {
+ _musicTimer = 0;
+ if (_musicFadeTimer) {
+ ++_musicFadeTimer;
+ if (_musicFadeTimer == 64) {
+ stopMusic();
+ } else {
+ if (_mixer->isSoundHandleActive(_moduleHandle)) {
+ _mixer->setChannelVolume(_moduleHandle, (64 - _musicFadeTimer) * Audio::Mixer::kMaxChannelVolume / 64);
+ }
+ }
+ }
+ }
+}
+
+const int PaulaSound::_channelBalance[NUM_CHANNELS] = {
+ // L/R/R/L This is according to the Hardware Reference Manual.
+ // TODO: It seems the order is swapped for some Amiga models:
+ // http://www.amiga.org/forums/archive/index.php/t-7862.html
+ // Maybe we should consider using R/L/L/R to match Amiga 500?
+ // This also is a bit more drastic to what WineUAE defaults,
+ // which is only 70% of full panning.
+ -127, 127, 127, -127
+};
+
} // End of namespace Cine
diff --git a/engines/cine/sound.h b/engines/cine/sound.h
index afc0994a26..fdb183ad34 100644
--- a/engines/cine/sound.h
+++ b/engines/cine/sound.h
@@ -24,6 +24,7 @@
#define CINE_SOUND_H_
#include "common/util.h"
+#include "common/mutex.h"
#include "audio/mixer.h"
namespace Audio {
@@ -47,7 +48,6 @@ public:
virtual void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) = 0;
virtual void stopSound(int channel) = 0;
- virtual void update() {}
protected:
@@ -91,19 +91,39 @@ public:
virtual void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat);
virtual void stopSound(int channel);
- virtual void update();
enum {
- PAULA_FREQ = 7093789,
- NUM_CHANNELS = 4,
- SPL_HDR_SIZE = 22
+ PAULA_FREQ = 3579545,
+ NUM_CHANNELS = 4
};
protected:
- void playSoundChannel(int channel, int frequency, uint8 *data, int size, int volume);
-
- Audio::SoundHandle _channelsTable[NUM_CHANNELS];
+ struct SfxChannel {
+ Audio::SoundHandle handle;
+ int volume;
+ int volumeStep;
+ int curStep;
+ int stepCount;
+
+ void initialize(int vol, int volStep, int stepCnt) {
+ volume = vol;
+ volumeStep = volStep;
+ curStep = stepCount = stepCnt;
+ }
+ };
+ SfxChannel _channelsTable[NUM_CHANNELS];
+ static const int _channelBalance[NUM_CHANNELS];
+ Common::Mutex _sfxMutex;
+ int _sfxTimer;
+ static void sfxTimerProc(void *param);
+ void sfxTimerCallback();
+
+ Common::Mutex _musicMutex;
+ int _musicTimer;
+ int _musicFadeTimer;
+ static void musicTimerProc(void *param);
+ void musicTimerCallback();
Audio::SoundHandle _moduleHandle;
Audio::AudioStream *_moduleStream;
};
diff --git a/engines/cine/texte.cpp b/engines/cine/texte.cpp
index 33ea569df7..998075c6ce 100644
--- a/engines/cine/texte.cpp
+++ b/engines/cine/texte.cpp
@@ -88,7 +88,7 @@ static const CharacterEntry fontParamTable_standard[NUM_FONT_CHARS] = {
{64, 3}, {65, 3}, { 0, 0}, { 0, 0}, {62, 2}, {74, 6}, {66, 1}, {67, 6},
{52, 6}, {53, 6}, {54, 6}, {55, 6}, {56, 6}, {57, 6}, {58, 6}, {59, 6},
{60, 6}, {61, 6}, {76, 3}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {75, 6},
- { 0, 0}, { 0, 6}, //a
+ { 0, 0}, { 0, 6}, //a
{ 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6},
{ 7, 6}, { 8, 3}, { 9, 6}, {10, 6}, {11, 6}, {12, 7}, {13, 6}, {14, 6},
{15, 6}, {16, 6}, {17, 6}, {18, 6}, {19, 6}, {20, 6}, {21, 6}, {22, 7},
diff --git a/engines/cine/texte.h b/engines/cine/texte.h
index dd4b7e06ee..185dc53bfd 100644
--- a/engines/cine/texte.h
+++ b/engines/cine/texte.h
@@ -46,7 +46,7 @@ struct CharacterEntry {
};
struct TextHandler {
- byte textTable[NUM_FONT_CHARS][2][FONT_WIDTH * FONT_HEIGHT];
+ byte textTable[NUM_FONT_CHARS][2][FONT_WIDTH *FONT_HEIGHT];
CharacterEntry fontParamTable[NUM_FONT_CHARS];
};
diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp
index eccd71cf05..23f439a7a7 100644
--- a/engines/cine/various.cpp
+++ b/engines/cine/various.cpp
@@ -99,8 +99,7 @@ byte isInPause = 0;
* Bit on = mouse button down
* Bit off = mouse button up
*/
-enum MouseButtonState
-{
+enum MouseButtonState {
kLeftMouseButton = (1 << 0),
kRightMouseButton = (1 << 1)
};
@@ -271,7 +270,7 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) {
} else if (it->type == 1 && gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].data(), g_cine->_animDataTable[frame]._width * 4)) {
return it->objIdx;
}
- } else if (it->type == 0) { // use generated mask
+ } else if (it->type == 0) { // use generated mask
if (gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].mask(), g_cine->_animDataTable[frame]._width)) {
return it->objIdx;
}
@@ -358,128 +357,122 @@ void CineEngine::makeSystemMenu() {
systemCommand = makeMenuChoice(systemMenu, numEntry, mouseX, mouseY, 140);
switch (systemCommand) {
- case 0: // Pause
- {
- renderer->drawString(otherMessages[2], 0);
- waitPlayerInput();
- break;
- }
- case 1: // Restart Game
- {
- getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
- if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
- _restartRequested = true;
- }
- break;
- }
- case 2: // Quit
- {
- getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
- if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
- quitGame();
- }
- break;
+ case 0: { // Pause
+ renderer->drawString(otherMessages[2], 0);
+ waitPlayerInput();
+ break;
+ }
+ case 1: { // Restart Game
+ getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
+ if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
+ _restartRequested = true;
}
- case 3: // Select save drive... change ?
- {
- break;
+ break;
+ }
+ case 2: { // Quit
+ getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
+ if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
+ quitGame();
}
- case 4: // load game
- {
- if (loadSaveDirectory()) {
+ break;
+ }
+ case 3: { // Select save drive... change ?
+ break;
+ }
+ case 4: { // load game
+ if (loadSaveDirectory()) {
// int16 selectedSave;
- getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
- selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180);
+ getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
+ selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180);
- if (selectedSave >= 0) {
- char saveNameBuffer[256];
- sprintf(saveNameBuffer, "%s.%1d", _targetName.c_str(), selectedSave);
+ if (selectedSave >= 0) {
+ char saveNameBuffer[256];
+ sprintf(saveNameBuffer, "%s.%1d", _targetName.c_str(), selectedSave);
- getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
- if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
- char loadString[256];
+ getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
+ if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
+ char loadString[256];
- sprintf(loadString, otherMessages[3], currentSaveName[selectedSave]);
- renderer->drawString(loadString, 0);
+ sprintf(loadString, otherMessages[3], currentSaveName[selectedSave]);
+ renderer->drawString(loadString, 0);
- makeLoad(saveNameBuffer);
- } else {
- renderer->drawString(otherMessages[4], 0);
- waitPlayerInput();
- checkDataDisk(-1);
- }
+ makeLoad(saveNameBuffer);
} else {
renderer->drawString(otherMessages[4], 0);
waitPlayerInput();
checkDataDisk(-1);
}
} else {
- renderer->drawString(otherMessages[5], 0);
+ renderer->drawString(otherMessages[4], 0);
waitPlayerInput();
checkDataDisk(-1);
}
- break;
+ } else {
+ renderer->drawString(otherMessages[5], 0);
+ waitPlayerInput();
+ checkDataDisk(-1);
}
- case 5: // Save game
- {
- loadSaveDirectory();
- selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180);
+ break;
+ }
+ case 5: { // Save game
+ loadSaveDirectory();
+ selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180);
- if (selectedSave >= 0) {
- char saveFileName[256];
- char saveName[20];
- saveName[0] = 0;
+ if (selectedSave >= 0) {
+ char saveFileName[256];
+ char saveName[20];
+ saveName[0] = 0;
- if (!makeTextEntryMenu(otherMessages[6], saveName, 20, 120))
- break;
+ if (!makeTextEntryMenu(otherMessages[6], saveName, 20, 120))
+ break;
- strncpy(currentSaveName[selectedSave], saveName, 20);
+ strncpy(currentSaveName[selectedSave], saveName, 20);
- sprintf(saveFileName, "%s.%1d", _targetName.c_str(), selectedSave);
+ sprintf(saveFileName, "%s.%1d", _targetName.c_str(), selectedSave);
- getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
- if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
- char saveString[256];
- Common::String tmp = Common::String::format("%s.dir", _targetName.c_str());
+ getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
+ if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
+ char saveString[256];
+ Common::String tmp = Common::String::format("%s.dir", _targetName.c_str());
- Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(tmp);
- if (!fHandle) {
- warning("Unable to open file %s for saving", tmp.c_str());
- break;
- }
+ Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(tmp);
+ if (!fHandle) {
+ warning("Unable to open file %s for saving", tmp.c_str());
+ break;
+ }
- fHandle->write(currentSaveName, 200);
- delete fHandle;
+ fHandle->write(currentSaveName, 200);
+ delete fHandle;
- sprintf(saveString, otherMessages[3], currentSaveName[selectedSave]);
- renderer->drawString(saveString, 0);
+ sprintf(saveString, otherMessages[3], currentSaveName[selectedSave]);
+ renderer->drawString(saveString, 0);
- makeSave(saveFileName);
+ makeSave(saveFileName);
- checkDataDisk(-1);
- } else {
- renderer->drawString(otherMessages[4], 0);
- waitPlayerInput();
- checkDataDisk(-1);
- }
+ checkDataDisk(-1);
+ } else {
+ renderer->drawString(otherMessages[4], 0);
+ waitPlayerInput();
+ checkDataDisk(-1);
}
- break;
}
+ break;
+ }
}
inMenu = false;
}
}
-void drawMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 offset, int16 color, byte* page) {
- gfxDrawLine(x + offset, y + offset, x + width - offset, y + offset, color, page); // top
- gfxDrawLine(x + offset, currentY + 4 - offset, x + width - offset, currentY + 4 - offset, color, page); // bottom
- gfxDrawLine(x + offset, y + offset, x + offset, currentY + 4 - offset, color, page); // left
- gfxDrawLine(x + width - offset, y + offset, x + width - offset, currentY + 4 - offset, color, page); // right
+void drawMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 offset, int16 color, byte *page) {
+ gfxDrawLine(x + offset, y + offset, x + width - offset, y + offset, color, page); // top
+ gfxDrawLine(x + offset, currentY + 4 - offset, x + width - offset, currentY + 4 - offset, color, page); // bottom
+ gfxDrawLine(x + offset, y + offset, x + offset, currentY + 4 - offset, color, page); // left
+ gfxDrawLine(x + width - offset, y + offset, x + width - offset, currentY + 4 - offset, color, page); // right
}
-void drawDoubleMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 color, byte* page) {
+void drawDoubleMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 color, byte *page) {
drawMessageBox(x, y, width, currentY, 1, 0, page);
drawMessageBox(x, y, width, currentY, 0, color, page);
}
@@ -581,7 +574,7 @@ void makeCommandLine() {
g_cine->_commandBuffer = "";
}
- if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection ?
+ if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection?
int16 si;
getMouseData(mouseUpdateStatus, &dummyU16, &x, &y);
@@ -635,7 +628,7 @@ void makeCommandLine() {
}
if (g_cine->getGameType() == Cine::GType_OS && playerCommand != 2) {
- if (playerCommand != -1 && canUseOnObject != 0) { // call use on sub object
+ if (playerCommand != -1 && canUseOnObject != 0) { // call use on sub object
int16 si;
getMouseData(mouseUpdateStatus, &dummyU16, &x, &y);
@@ -742,11 +735,11 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X,
mainLoopSub6();
}
- if (menuVar4 && currentSelection > 0) { // go up
+ if (menuVar4 && currentSelection > 0) { // go up
currentSelection--;
}
- if (menuVar5) { // go down
+ if (menuVar5) { // go down
if (height - 1 > currentSelection) {
currentSelection++;
}
@@ -763,7 +756,7 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X,
}
}
- if (currentSelection != oldSelection) { // old != new
+ if (currentSelection != oldSelection) { // old != new
if (needMouseSave) {
hideMouse();
}
@@ -789,7 +782,7 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X,
getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16);
} while (button && !g_cine->shouldQuit());
- if (var_4 == 2) { // recheck
+ if (var_4 == 2) { // recheck
if (!recheckValue)
return -1;
else
@@ -809,7 +802,7 @@ void makeActionMenu() {
getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);
if (g_cine->getGameType() == Cine::GType_OS) {
- if(disableSystemMenu == 0) {
+ if (disableSystemMenu == 0) {
playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70, true);
}
@@ -818,7 +811,7 @@ void makeActionMenu() {
canUseOnObject = canUseOnItemTable[playerCommand];
}
} else {
- if(disableSystemMenu == 0) {
+ if (disableSystemMenu == 0) {
playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70);
}
}
@@ -1185,7 +1178,7 @@ void removeMessages() {
Common::List<overlay>::iterator it;
bool remove;
- for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end();) {
if (g_cine->getGameType() == Cine::GType_OS) {
// NOTE: These are really removeOverlay calls that have been deferred.
// In Operation Stealth's disassembly elements are removed from the
@@ -1348,7 +1341,7 @@ void modifySeqListElement(uint16 objIdx, int16 var4Test, int16 param1, int16 par
}
void computeMove1(SeqListElement &element, int16 x, int16 y, int16 param1,
- int16 param2, int16 x2, int16 y2) {
+ int16 param2, int16 x2, int16 y2) {
element.var16 = 0;
element.var14 = 0;
@@ -1397,7 +1390,7 @@ uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &ele
int16 di;
debug(5, "addAni: param1 = %d, objIdx = %d, ptr = %p, element.var8 = %d, element.var14 = %d param3 = %d",
- param1, objIdx, ptr, element.var8, element.var14, param3);
+ param1, objIdx, ptr, element.var8, element.var14, param3);
// In the original an error string is set and 0 is returned if the following doesn't hold
assert(ptr);
@@ -1413,16 +1406,16 @@ uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &ele
di = (g_cine->_objectTable[objIdx].costume + 1) % (*ptrData);
++ptrData; // Jump over the just read byte
// Here ptr2 seems to be indexing a table of structs (8 bytes per struct):
- // struct {
- // int8 x; // 0 (Used with checkCollision)
- // int8 y; // 1 (Used with checkCollision)
- // int8 numZones; // 2 (Used with checkCollision)
- // int8 var3; // 3 (Not used in this function)
- // int8 xAdd; // 4 (Used with an object)
- // int8 yAdd; // 5 (Used with an object)
- // int8 maskAdd; // 6 (Used with an object)
- // int8 frameAdd; // 7 (Used with an object)
- // };
+ // struct {
+ // int8 x; // 0 (Used with checkCollision)
+ // int8 y; // 1 (Used with checkCollision)
+ // int8 numZones; // 2 (Used with checkCollision)
+ // int8 var3; // 3 (Not used in this function)
+ // int8 xAdd; // 4 (Used with an object)
+ // int8 yAdd; // 5 (Used with an object)
+ // int8 maskAdd; // 6 (Used with an object)
+ // int8 frameAdd; // 7 (Used with an object)
+ // };
ptr2 = ptrData + di * 8;
// We might probably safely discard the AND by 1 here because
@@ -1572,8 +1565,7 @@ void processSeqListElement(SeqListElement &element) {
var_4 = -1;
if ((element.var16 == 1
- && !addAni(3, element.objIdx, ptr1, element, 0, &var_4)) || (element.var16 == 2 && !addAni(2, element.objIdx, ptr1, element, 0,
- &var_4))) {
+ && !addAni(3, element.objIdx, ptr1, element, 0, &var_4)) || (element.var16 == 2 && !addAni(2, element.objIdx, ptr1, element, 0, &var_4))) {
if (element.varC == 255) {
g_cine->_globalVars[VAR_MOUSE_Y_POS] = 0;
}
@@ -1702,9 +1694,9 @@ bool makeTextEntryMenu(const char *messagePtr, char *inputString, int stringMaxL
}
break;
default:
- if (((keycode >= 'a') && (keycode <='z')) ||
- ((keycode >= '0') && (keycode <='9')) ||
- ((keycode >= 'A') && (keycode <='Z')) ||
+ if (((keycode >= 'a') && (keycode <= 'z')) ||
+ ((keycode >= '0') && (keycode <= '9')) ||
+ ((keycode >= 'A') && (keycode <= 'Z')) ||
(keycode == ' ')) {
if (inputLength < stringMaxLength - 1) {
ch[0] = keycode;
diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp
index 23a9d2ff85..555b703da9 100644
--- a/engines/composer/composer.cpp
+++ b/engines/composer/composer.cpp
@@ -20,7 +20,7 @@
*
*/
#include "common/scummsys.h"
-
+
#include "common/config-manager.h"
#include "common/events.h"
#include "common/file.h"
diff --git a/engines/composer/resource.cpp b/engines/composer/resource.cpp
index a4e292747c..83e49971fb 100644
--- a/engines/composer/resource.cpp
+++ b/engines/composer/resource.cpp
@@ -240,7 +240,7 @@ bool ComposerArchive::openStream(Common::SeekableReadStream *stream) {
res.flags = flags;
debug(4, "Id %d, offset %d, size %d, flags %08x", id, offset, size, flags);
}
-
+
stream->seek(oldPos);
}
diff --git a/engines/configure.engines b/engines/configure.engines
index 8eaee730cc..a8eb89a7fa 100644
--- a/engines/configure.engines
+++ b/engines/configure.engines
@@ -1,9 +1,10 @@
# This file is included from the main "configure" script
-add_engine scumm "SCUMM" yes "scumm_7_8 he"
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine scumm "SCUMM" yes "scumm_7_8 he" "v0-v6 games"
add_engine scumm_7_8 "v7 & v8 games" yes
add_engine he "HE71+ games" yes
add_engine agi "AGI" yes
-add_engine agos "AGOS" yes "agos2"
+add_engine agos "AGOS" yes "agos2" "AGOS 1 games"
add_engine agos2 "AGOS 2 games" yes
add_engine cge "CGE" yes
add_engine cine "Cinematique evo 1" yes
@@ -13,35 +14,38 @@ add_engine draci "Dragon History" yes
add_engine drascula "Drascula: The Vampire Strikes Back" yes
add_engine dreamweb "Dreamweb" yes
add_engine gob "Gobli*ns" yes
-add_engine groovie "Groovie" yes "groovie2"
+add_engine groovie "Groovie" yes "groovie2" "7th Guest"
add_engine groovie2 "Groovie 2 games" no
add_engine hugo "Hugo Trilogy" yes
-add_engine kyra "Legend of Kyrandia" yes "lol eob"
+add_engine kyra "Kyra" yes "lol eob" "Legend of Kyrandia 1-3"
add_engine lol "Lands of Lore" yes
add_engine eob "Eye of the Beholder" no
-add_engine lastexpress "The Last Express" no
+add_engine lastexpress "The Last Express" no "" "" "16bit"
add_engine lure "Lure of the Temptress" yes
add_engine made "MADE" yes
-add_engine mohawk "Mohawk" yes "cstime myst riven"
+add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books"
add_engine cstime "Where in Time is Carmen Sandiego?" no
-add_engine riven "Riven: The Sequel to Myst" no
-add_engine myst "Myst" no
+add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit"
+add_engine myst "Myst" no "" "" "16bit"
add_engine parallaction "Parallaction" yes
+add_engine pegasus "The Journeyman Project: Pegasus Prime" no "" "" "16bit"
add_engine queen "Flight of the Amazon Queen" yes
-add_engine saga "SAGA" yes "ihnm saga2"
+add_engine saga "SAGA" yes "ihnm saga2" "ITE"
add_engine ihnm "IHNM" yes
add_engine saga2 "SAGA 2 games" no
-add_engine sci "SCI" yes "sci32"
+add_engine sci "SCI" yes "sci32" "SCI 0-1.1 games"
add_engine sci32 "SCI32 games" no
add_engine sky "Beneath a Steel Sky" yes
add_engine sword1 "Broken Sword" yes
add_engine sword2 "Broken Sword II" yes
-add_engine sword25 "Broken Sword 2.5" no
+add_engine sword25 "Broken Sword 2.5" no "" "" "png zlib 16bit"
add_engine teenagent "Teen Agent" yes
add_engine testbed "TestBed: the Testing framework" no
add_engine tinsel "Tinsel" yes
add_engine toltecs "3 Skulls of the Toltecs" no
add_engine toon "Toonstruck" yes
add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes
+add_engine tony "Tony Tough and the Night of Roasted Moths" no "" "" "16bit"
add_engine tsage "TsAGE" yes
add_engine tucker "Bud Tucker in Double Trouble" yes
+add_engine wintermute "Wintermute" no "" "" "png zlib vorbis 16bit"
diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp
index cbe17ea4d3..ba79df4822 100644
--- a/engines/cruise/detection.cpp
+++ b/engines/cruise/detection.cpp
@@ -295,7 +295,7 @@ void CruiseMetaEngine::removeSaveState(const char *target, int slot) const {
SaveStateDescriptor CruiseMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(
Cruise::CruiseEngine::getSavegameFile(slot));
-
+
if (f) {
Cruise::CruiseSavegameHeader header;
Cruise::readSavegameHeader(f, header);
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index cf3dfaa44b..c884075093 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -229,7 +229,7 @@ void MainMenuDialog::save() {
"Please consult the README for basic information, and for "
"instructions on how to obtain further assistance."), status.getDesc().c_str());
GUI::MessageDialog dialog(failMessage);
- dialog.runModal();
+ dialog.runModal();
}
close();
diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp
index 61705a1e59..2d78d05933 100644
--- a/engines/draci/detection.cpp
+++ b/engines/draci/detection.cpp
@@ -153,7 +153,7 @@ void DraciMetaEngine::removeSaveState(const char *target, int slot) const {
SaveStateDescriptor DraciMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(
Draci::DraciEngine::getSavegameFile(slot));
-
+
if (f) {
Draci::DraciSavegameHeader header;
Draci::readSavegameHeader(f, header);
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index 6e38d49b94..760d8b7d98 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -284,7 +284,7 @@ public:
Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
Common::Array<int> slots;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
-
+
// Obtain the last 2 digits of the filename, since they correspond to the save slot
int slotNum = atoi(file->c_str() + file->size() - 2);
@@ -299,7 +299,7 @@ public:
// Load save index
Common::String fileEpa = Common::String::format("%s.epa", target);
- Common::InSaveFile *epa = saveFileMan->openForLoading(fileEpa);
+ Common::InSaveFile *epa = saveFileMan->openForLoading(fileEpa);
// Get savegame names from index
Common::String saveDesc;
@@ -307,19 +307,19 @@ public:
int line = 1;
for (size_t i = 0; i < slots.size(); i++) {
// ignore lines corresponding to unused saveslots
- for (; line < slots[i]; line++)
- epa->readLine();
+ for (; line < slots[i]; line++)
+ epa->readLine();
// copy the name in the line corresponding to the save slot and truncate to 22 characters
saveDesc = Common::String(epa->readLine().c_str(), 22);
// handle cases where the save directory and save index are detectably out of sync
- if (saveDesc == "*")
+ if (saveDesc == "*")
saveDesc = "No name specified.";
// increment line number to keep it in sync with slot number
- line++;
-
+ line++;
+
// Insert savegame name into list
saveList.push_back(SaveStateDescriptor(slots[i], saveDesc));
}
diff --git a/engines/dreamweb/dreamweb.cpp b/engines/dreamweb/dreamweb.cpp
index 0ca98d5a7b..5f5d627553 100644
--- a/engines/dreamweb/dreamweb.cpp
+++ b/engines/dreamweb/dreamweb.cpp
@@ -516,7 +516,7 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const {
case Common::IT_ITA:
switch(c) {
case 133:
- return 'Z' + 1;
+ return 'Z' + 1;
case 130:
return 'Z' + 2;
case 138:
@@ -548,10 +548,10 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const {
return c;
}
}
-
+
Common::String DreamWebEngine::modifyFileName(const char *name) {
Common::String fileName(name);
-
+
// Sanity check
if (!fileName.hasPrefix("DREAMWEB."))
return fileName;
diff --git a/engines/dreamweb/dreamweb.h b/engines/dreamweb/dreamweb.h
index 1f6deb8566..a4597b1867 100644
--- a/engines/dreamweb/dreamweb.h
+++ b/engines/dreamweb/dreamweb.h
@@ -196,7 +196,7 @@ protected:
// from monitor.cpp
char _inputLine[64];
- char _operand1[14];
+ char _operand1[64];
char _currentFile[14];
// from newplace.cpp
diff --git a/engines/dreamweb/monitor.cpp b/engines/dreamweb/monitor.cpp
index 4e9d8eecc1..1886a80b6a 100644
--- a/engines/dreamweb/monitor.cpp
+++ b/engines/dreamweb/monitor.cpp
@@ -194,7 +194,7 @@ void DreamWebEngine::printLogo() {
}
void DreamWebEngine::input() {
- memset(_inputLine, 0, 64);
+ memset(_inputLine, 0, sizeof(_inputLine));
_curPos = 0;
printChar(_monitorCharset, _monAdX, _monAdY, '>', 0, NULL, NULL);
multiDump(_monAdX, _monAdY, 6, 8);
@@ -665,7 +665,7 @@ void DreamWebEngine::searchForFiles(const char *filesString) {
const char *DreamWebEngine::parser() {
char *output = _operand1;
- memset(output, 0, 14);
+ memset(output, 0, sizeof(_operand1));
*output++ = '=';
diff --git a/engines/dreamweb/object.cpp b/engines/dreamweb/object.cpp
index b42591ef91..1e84aba6bd 100644
--- a/engines/dreamweb/object.cpp
+++ b/engines/dreamweb/object.cpp
@@ -516,7 +516,7 @@ void DreamWebEngine::inToInv() {
if (_mouseButton == _oldButton || !(_mouseButton & 1))
return; // notletgo2
-
+
delPointer();
DynObject *object = getExAd(_itemFrame);
object->mapad[0] = 4;
@@ -1034,7 +1034,7 @@ void DreamWebEngine::fillOpen() {
size = 4;
findAllOpen();
for (uint8 i = 0; i < size; ++i) {
- uint8 index = _openInvList[i]._index;
+ uint8 index = _openInvList[i]._index;
uint8 type = _openInvList[i]._type;
obToInv(index, type, kInventx + i * kItempicsize, kInventy + 96);
}
diff --git a/engines/dreamweb/people.cpp b/engines/dreamweb/people.cpp
index 36a756a49b..dbb81406cd 100644
--- a/engines/dreamweb/people.cpp
+++ b/engines/dreamweb/people.cpp
@@ -411,7 +411,7 @@ void DreamWebEngine::interviewer(ReelRoutine &routine) {
if (routine.reelPointer() != 250 && routine.reelPointer() != 259 && checkSpeed(routine))
routine.incReelPointer();
-
+
showGameReel(&routine);
}
@@ -745,7 +745,7 @@ void DreamWebEngine::introMonks2(ReelRoutine &routine) {
if (nextReelPointer == 110) {
_introCount++;
monks2text();
-
+
if (_introCount == 35)
nextReelPointer = 111;
else
@@ -895,7 +895,7 @@ void DreamWebEngine::helicopter(ReelRoutine &routine) {
nextReelPointer = 9;
}
}
- }
+ }
routine.setReelPointer(nextReelPointer);
}
@@ -1002,7 +1002,7 @@ void DreamWebEngine::businessMan(ReelRoutine &routine) {
nextReelPointer = 92;
}
}
-
+
routine.setReelPointer(nextReelPointer);
}
@@ -1037,7 +1037,7 @@ void DreamWebEngine::endGameSeq(ReelRoutine &routine) {
showGameReel(&routine);
routine.mapY = _mapY;
-
+
if (routine.reelPointer() == 145) {
routine.setReelPointer(146);
rollEndCreditsGameWon();
@@ -1070,7 +1070,7 @@ void DreamWebEngine::poolGuard(ReelRoutine &routine) {
if (checkSpeed(routine)) {
uint16 nextReelPointer = routine.reelPointer() + 1;
-
+
if (nextReelPointer != 122) {
// Not end guard 1
if (nextReelPointer == 147) {
@@ -1103,12 +1103,12 @@ void DreamWebEngine::poolGuard(ReelRoutine &routine) {
}
}
}
-
+
routine.setReelPointer(nextReelPointer);
}
showGameReel(&routine);
-
+
if (routine.reelPointer() != 121 && routine.reelPointer() != 146) {
_pointerMode = 0;
_vars._watchingTime = 2;
diff --git a/engines/dreamweb/print.cpp b/engines/dreamweb/print.cpp
index 3a2c45e07b..64b9849980 100644
--- a/engines/dreamweb/print.cpp
+++ b/engines/dreamweb/print.cpp
@@ -212,7 +212,7 @@ const char *DreamWebEngine::monPrint(const char *string) {
while (!done) {
uint16 count = getNumber(_monitorCharset, (const uint8 *)iterator, 166, false, &x);
- do {
+ do {
char c = *iterator++;
if (c == ':')
break;
diff --git a/engines/dreamweb/sprite.cpp b/engines/dreamweb/sprite.cpp
index 5b6cf6a6ac..01570c907a 100644
--- a/engines/dreamweb/sprite.cpp
+++ b/engines/dreamweb/sprite.cpp
@@ -52,7 +52,7 @@ void DreamWebEngine::printASprite(const Sprite *sprite) {
} else {
x = sprite->x + _mapAdX;
}
-
+
uint8 c;
if (sprite->walkFrame != 0)
c = 8;
@@ -97,7 +97,7 @@ void DreamWebEngine::spriteUpdate() {
else {
backObject(&sprite);
}
-
+
if (_nowInNewRoom == 1)
break;
}
@@ -373,7 +373,7 @@ void DreamWebEngine::lockedDoorway(Sprite *sprite, SetObject *objData) {
if (sprite->animFrame != 0)
--sprite->animFrame;
-
+
_vars._throughDoor = 0;
sprite->frameNumber = objData->index = objData->frames[sprite->animFrame];
@@ -407,7 +407,7 @@ void DreamWebEngine::liftSprite(Sprite *sprite, SetObject *objData) {
}
sprite->animFrame = 12;
sprite->frameNumber = objData->index = objData->frames[sprite->animFrame];
- }
+ }
else if (liftFlag == 3) { //openlift
if (sprite->animFrame == 12) {
_vars._liftFlag = 1;
@@ -672,7 +672,7 @@ static const ReelSound g_roomSound6[] = {
{ 255,0 }
};
static const ReelSound g_roomSound8[] = {
-
+
{ 12, 51 },
{ 13, 53 },
{ 14, 14 },
@@ -691,7 +691,7 @@ static const ReelSound g_roomSound10[] = {
{ 13, 16 },
{ 255,0 }
};
-
+
static const ReelSound g_roomSound11[] = {
{ 13, 20 },
{ 255,0 }
@@ -779,7 +779,7 @@ static const ReelSound g_roomSound26[] = {
{ 15, 102 }, // was 90, should be mine cart
{ 255,0 }
};
-
+
static const ReelSound g_roomSound27[] = {
{ 22, 36 },
{ 13, 125 },
diff --git a/engines/dreamweb/vgagrafx.cpp b/engines/dreamweb/vgagrafx.cpp
index ec306c4924..d2390fb1fd 100644
--- a/engines/dreamweb/vgagrafx.cpp
+++ b/engines/dreamweb/vgagrafx.cpp
@@ -23,6 +23,7 @@
#include "dreamweb/dreamweb.h"
#include "engines/util.h"
#include "graphics/surface.h"
+#include "graphics/decoders/pcx.h"
namespace DreamWeb {
@@ -152,70 +153,33 @@ void DreamWebEngine::setMode() {
void DreamWebEngine::showPCX(const Common::String &suffix) {
Common::String name = getDatafilePrefix() + suffix;
Common::File pcxFile;
-
if (!pcxFile.open(name)) {
warning("showpcx: Could not open '%s'", name.c_str());
return;
}
- uint8 *mainGamePal;
- int i, j;
+ Graphics::PCXDecoder pcx;
+ if (!pcx.loadStream(pcxFile)) {
+ warning("showpcx: Could not process '%s'", name.c_str());
+ return;
+ }
// Read the 16-color palette into the 'maingamepal' buffer. Note that
// the color components have to be adjusted from 8 to 6 bits.
-
- pcxFile.seek(16, SEEK_SET);
- mainGamePal = _mainPal;
- pcxFile.read(mainGamePal, 48);
-
- memset(mainGamePal + 48, 0xff, 720);
- for (i = 0; i < 48; i++) {
- mainGamePal[i] >>= 2;
+ memset(_mainPal, 0xff, 256 * 3);
+ memcpy(_mainPal, pcx.getPalette(), 48);
+ for (int i = 0; i < 48; i++) {
+ _mainPal[i] >>= 2;
}
- // Decode the image data.
-
Graphics::Surface *s = g_system->lockScreen();
- Common::Rect rect(640, 480);
-
- s->fillRect(rect, 0);
- pcxFile.seek(128, SEEK_SET);
-
- for (int y = 0; y < 480; y++) {
- byte *dst = (byte *)s->getBasePtr(0, y);
- int decoded = 0;
-
- while (decoded < 320) {
- byte col = pcxFile.readByte();
- byte len;
-
- if ((col & 0xc0) == 0xc0) {
- len = col & 0x3f;
- col = pcxFile.readByte();
- } else {
- len = 1;
- }
-
- // The image uses 16 colors and is stored as four bit
- // planes, one for each bit of the color, least
- // significant bit plane first.
-
- for (i = 0; i < len; i++) {
- int plane = decoded / 80;
- int pos = decoded % 80;
-
- for (j = 0; j < 8; j++) {
- byte bit = (col >> (7 - j)) & 1;
- dst[8 * pos + j] |= (bit << plane);
- }
-
- decoded++;
- }
- }
- }
-
+ s->fillRect(Common::Rect(640, 480), 0);
+ const Graphics::Surface *pcxSurface = pcx.getSurface();
+ if (pcxSurface->format.bytesPerPixel != 1)
+ error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel);
+ for (uint16 y = 0; y < pcxSurface->h; y++)
+ memcpy((byte *)s->getBasePtr(0, y), pcxSurface->getBasePtr(0, y), pcxSurface->w);
g_system->unlockScreen();
- pcxFile.close();
}
void DreamWebEngine::frameOutV(uint8 *dst, const uint8 *src, uint16 pitch, uint16 width, uint16 height, int16 x, int16 y) {
@@ -370,9 +334,9 @@ void DreamWebEngine::zoom() {
for (size_t j = 0; j < 23; ++j) {
uint8 v = src[j];
dst[2*j+0] = v;
- dst[2*j+1] = v;
+ dst[2*j+1] = v;
dst[2*j+320] = v;
- dst[2*j+321] = v;
+ dst[2*j+321] = v;
}
src += 320;
dst += 320*2;
diff --git a/engines/engines.mk b/engines/engines.mk
index 9939506b86..61004463fe 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -130,6 +130,11 @@ DEFINES += -DENABLE_PARALLACTION=$(ENABLE_PARALLACTION)
MODULES += engines/parallaction
endif
+ifdef ENABLE_PEGASUS
+DEFINES += -DENABLE_PEGASUS=$(ENABLE_PEGASUS)
+MODULES += engines/pegasus
+endif
+
ifdef ENABLE_QUEEN
DEFINES += -DENABLE_QUEEN=$(ENABLE_QUEEN)
MODULES += engines/queen
@@ -197,6 +202,11 @@ DEFINES += -DENABLE_TOLTECS=$(ENABLE_TOLTECS)
MODULES += engines/toltecs
endif
+ifdef ENABLE_TONY
+DEFINES += -DENABLE_TONY=$(ENABLE_TONY)
+MODULES += engines/tony
+endif
+
ifdef ENABLE_TOON
DEFINES += -DENABLE_TOON=$(ENABLE_TOON)
MODULES += engines/toon
@@ -216,3 +226,8 @@ ifdef ENABLE_TUCKER
DEFINES += -DENABLE_TUCKER=$(ENABLE_TUCKER)
MODULES += engines/tucker
endif
+
+ifdef ENABLE_WINTERMUTE
+DEFINES += -DENABLE_WINTERMUTE=$(ENABLE_WINTERMUTE)
+MODULES += engines/wintermute
+endif
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index 1a3b313649..895686b5e0 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -142,7 +142,7 @@ static const GroovieGameDescription gameDescriptions[] = {
{
"11h", "Demo",
AD_ENTRY1s("disk.1", "aacb32ce07e0df2894bd83a3dee40c12", 70),
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE,
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE,
GUIO5(GUIO_NOLAUNCHLOAD, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
},
kGroovieV2, 1
diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp
index 35d7ecf886..72a61fefb2 100644
--- a/engines/groovie/roq.cpp
+++ b/engines/groovie/roq.cpp
@@ -288,19 +288,18 @@ bool ROQPlayer::processBlockInfo(ROQBlockHeader &blockHeader) {
// them it should be just fine.
_currBuf->create(width, height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0));
_prevBuf->create(width, height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0));
+ }
- // Clear the buffers with black YUV values
- byte *ptr1 = (byte *)_currBuf->getBasePtr(0, 0);
- byte *ptr2 = (byte *)_prevBuf->getBasePtr(0, 0);
- for (int i = 0; i < width * height; i++) {
- *ptr1++ = 0;
- *ptr1++ = 128;
- *ptr1++ = 128;
- *ptr2++ = 0;
- *ptr2++ = 128;
- *ptr2++ = 128;
- }
-
+ // Clear the buffers with black YUV values
+ byte *ptr1 = (byte *)_currBuf->getBasePtr(0, 0);
+ byte *ptr2 = (byte *)_prevBuf->getBasePtr(0, 0);
+ for (int i = 0; i < width * height; i++) {
+ *ptr1++ = 0;
+ *ptr1++ = 128;
+ *ptr1++ = 128;
+ *ptr2++ = 0;
+ *ptr2++ = 128;
+ *ptr2++ = 128;
}
return true;
@@ -405,7 +404,7 @@ void ROQPlayer::processBlockQuadVectorBlock(int baseX, int baseY, int8 Mx, int8
}
void ROQPlayer::processBlockQuadVectorBlockSub(int baseX, int baseY, int8 Mx, int8 My) {
- debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block");
+ debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block");
uint16 codingType = getCodingType();
switch (codingType) {
@@ -433,7 +432,7 @@ void ROQPlayer::processBlockQuadVectorBlockSub(int baseX, int baseY, int8 Mx, in
bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing still (JPEG) block");
- warning("Groovie::ROQ: JPEG frame (unfinshed)");
+ warning("Groovie::ROQ: JPEG frame (unfinished)");
Graphics::JPEGDecoder *jpg = new Graphics::JPEGDecoder();
jpg->loadStream(*_file);
diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp
index 15ee06c82a..1758f3f6a5 100644
--- a/engines/hugo/file.cpp
+++ b/engines/hugo/file.cpp
@@ -32,7 +32,11 @@
#include "common/savefile.h"
#include "common/textconsole.h"
#include "common/config-manager.h"
+
+#include "graphics/surface.h"
+#include "graphics/decoders/pcx.h"
#include "graphics/thumbnail.h"
+
#include "gui/saveload.h"
#include "hugo/hugo.h"
@@ -88,66 +92,33 @@ const char *FileManager::getUifFilename() const {
}
/**
- * Convert 4 planes (RGBI) data to 8-bit DIB format
- * Return original plane data ptr
- */
-byte *FileManager::convertPCC(byte *p, const uint16 y, const uint16 bpl, ImagePtr dataPtr) const {
- debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, ImagePtr dataPtr)", y, bpl);
-
- dataPtr += y * bpl * 8; // Point to correct DIB line
- for (int16 r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) { // Each byte in all planes
- for (int8 bit = 7; bit >= 0; bit--) { // Each bit in byte
- *dataPtr++ = (((p[r] >> bit & 1) << 0) |
- ((p[g] >> bit & 1) << 1) |
- ((p[b] >> bit & 1) << 2) |
- ((p[i] >> bit & 1) << 3));
- }
- }
- return p;
-}
-
-/**
* Read a pcx file of length len. Use supplied seqPtr and image_p or
* allocate space if NULL. Name used for errors. Returns address of seqPtr
* Set first TRUE to initialize b_index (i.e. not reading a sequential image in file).
*/
-Seq *FileManager::readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name) {
+Seq *FileManager::readPCX(Common::SeekableReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name) {
debugC(1, kDebugFile, "readPCX(..., %s)", name);
- // Read in the PCC header and check consistency
- _PCCHeader._mfctr = f.readByte();
- _PCCHeader._vers = f.readByte();
- _PCCHeader._enc = f.readByte();
- _PCCHeader._bpx = f.readByte();
- _PCCHeader._x1 = f.readUint16LE();
- _PCCHeader._y1 = f.readUint16LE();
- _PCCHeader._x2 = f.readUint16LE();
- _PCCHeader._y2 = f.readUint16LE();
- _PCCHeader._xres = f.readUint16LE();
- _PCCHeader._yres = f.readUint16LE();
- f.read(_PCCHeader._palette, sizeof(_PCCHeader._palette));
- _PCCHeader._vmode = f.readByte();
- _PCCHeader._planes = f.readByte();
- _PCCHeader._bytesPerLine = f.readUint16LE();
- f.read(_PCCHeader._fill2, sizeof(_PCCHeader._fill2));
-
- if (_PCCHeader._mfctr != 10)
- error("Bad data file format: %s", name);
-
// Allocate memory for Seq if 0
if (seqPtr == 0) {
if ((seqPtr = (Seq *)malloc(sizeof(Seq))) == 0)
error("Insufficient memory to run game.");
}
+ Graphics::PCXDecoder pcx;
+ if (!pcx.loadStream(f))
+ error("Error while reading PCX image");
+
+ const Graphics::Surface *pcxSurface = pcx.getSurface();
+ if (pcxSurface->format.bytesPerPixel != 1)
+ error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel);
+
// Find size of image data in 8-bit DIB format
// Note save of x2 - marks end of valid data before garbage
- uint16 bytesPerLine4 = _PCCHeader._bytesPerLine * 4; // 4-bit bpl
- seqPtr->_bytesPerLine8 = bytesPerLine4 * 2; // 8-bit bpl
- seqPtr->_lines = _PCCHeader._y2 - _PCCHeader._y1 + 1;
- seqPtr->_x2 = _PCCHeader._x2 - _PCCHeader._x1 + 1;
+ seqPtr->_lines = pcxSurface->h;
+ seqPtr->_x2 = seqPtr->_bytesPerLine8 = pcxSurface->w;
// Size of the image
- uint16 size = seqPtr->_lines * seqPtr->_bytesPerLine8;
+ uint16 size = pcxSurface->w * pcxSurface->h;
// Allocate memory for image data if NULL
if (imagePtr == 0)
@@ -156,26 +127,9 @@ Seq *FileManager::readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, co
assert(imagePtr);
seqPtr->_imagePtr = imagePtr;
+ for (uint16 y = 0; y < pcxSurface->h; y++)
+ memcpy(imagePtr + y * pcxSurface->w, pcxSurface->getBasePtr(0, y), pcxSurface->w);
- // Process the image data, converting to 8-bit DIB format
- uint16 y = 0; // Current line index
- byte pline[kXPix]; // Hold 4 planes of data
- byte *p = pline; // Ptr to above
- while (y < seqPtr->_lines) {
- byte c = f.readByte();
- if ((c & kRepeatMask) == kRepeatMask) {
- byte d = f.readByte(); // Read data byte
- for (int i = 0; i < (c & kLengthMask); i++) {
- *p++ = d;
- if ((uint16)(p - pline) == bytesPerLine4)
- p = convertPCC(pline, y++, _PCCHeader._bytesPerLine, imagePtr);
- }
- } else {
- *p++ = c;
- if ((uint16)(p - pline) == bytesPerLine4)
- p = convertPCC(pline, y++, _PCCHeader._bytesPerLine, imagePtr);
- }
- }
return seqPtr;
}
diff --git a/engines/hugo/file.h b/engines/hugo/file.h
index 1438bd2054..44f257a2af 100644
--- a/engines/hugo/file.h
+++ b/engines/hugo/file.h
@@ -112,16 +112,13 @@ protected:
Common::File _sceneryArchive1; // Handle for scenery file
Common::File _objectsArchive; // Handle for objects file
- PCCHeader _PCCHeader;
-
- Seq *readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name);
+ Seq *readPCX(Common::SeekableReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name);
// If this is the first call, read the lookup table
bool _hasReadHeader;
SoundHdr _soundHdr[kMaxSounds]; // Sound lookup table
private:
- byte *convertPCC(byte *p, const uint16 y, const uint16 bpl, ImagePtr dataPtr) const;
UifHdr *getUIFHeader(const Uif id);
};
diff --git a/engines/lastexpress/data/background.cpp b/engines/lastexpress/data/background.cpp
index 3d866c26f9..60379251a3 100644
--- a/engines/lastexpress/data/background.cpp
+++ b/engines/lastexpress/data/background.cpp
@@ -107,6 +107,7 @@ byte *Background::decodeComponent(Common::SeekableReadStream *in, uint32 inSize,
return NULL;
// Initialize the decoding
+ memset(out, 0, outSize * sizeof(byte));
uint32 inPos = 0;
uint32 outPos = 0;
diff --git a/engines/lastexpress/data/subtitle.cpp b/engines/lastexpress/data/subtitle.cpp
index a9a8284588..4d19c02aa7 100644
--- a/engines/lastexpress/data/subtitle.cpp
+++ b/engines/lastexpress/data/subtitle.cpp
@@ -210,10 +210,10 @@ void SubtitleManager::setTime(uint16 time) {
_currentIndex = -1;
// Find the appropriate line to show
- for (int16 i = 0; i < (int16)_subtitles.size(); i++) {
+ for (uint i = 0; i < _subtitles.size(); i++) {
if ((time >= _subtitles[i]->getTimeStart()) && (time <= _subtitles[i]->getTimeStop())) {
// Keep the index of the line to show
- _currentIndex = i;
+ _currentIndex = (int16)i;
return;
}
}
@@ -237,7 +237,7 @@ Common::Rect SubtitleManager::draw(Graphics::Surface *surface) {
// Draw the current line
assert(_currentIndex >= 0 && _currentIndex < (int16)_subtitles.size());
- return _subtitles[_currentIndex]->draw(surface, _font);
+ return _subtitles[(uint16)_currentIndex]->draw(surface, _font);
}
} // End of namespace LastExpress
diff --git a/engines/lastexpress/debug.cpp b/engines/lastexpress/debug.cpp
index f89ad8b80d..db3a3e3962 100644
--- a/engines/lastexpress/debug.cpp
+++ b/engines/lastexpress/debug.cpp
@@ -85,7 +85,6 @@ Debugger::Debugger(LastExpressEngine *engine) : _engine(engine), _command(NULL),
DCmd_Register("entity", WRAP_METHOD(Debugger, cmdEntity));
// Misc
- DCmd_Register("loadgame", WRAP_METHOD(Debugger, cmdLoadGame));
DCmd_Register("chapter", WRAP_METHOD(Debugger, cmdSwitchChapter));
DCmd_Register("clear", WRAP_METHOD(Debugger, cmdClear));
@@ -139,6 +138,9 @@ void Debugger::copyCommand(int argc, const char **argv) {
for (int i = 0; i < _numParams; i++) {
_commandParams[i] = (char *)malloc(strlen(argv[i]) + 1);
+ if (_commandParams[i] == NULL)
+ error("[Debugger::copyCommand] Cannot allocate memory for command parameters");
+
memset(_commandParams[i], 0, strlen(argv[i]) + 1);
strcpy(_commandParams[i], argv[i]);
}
@@ -152,9 +154,18 @@ void Debugger::callCommand() {
(*_command)(_numParams, const_cast<const char **>(_commandParams));
}
-void Debugger::loadArchive(ArchiveIndex index) const {
- _engine->getResourceManager()->loadArchive(index);
- getScenes()->loadSceneDataFile(index);
+bool Debugger::loadArchive(int index) {
+ if (index < 1 || index > 3) {
+ DebugPrintf("Invalid cd number (was: %d, valid: [1-3])\n", index);
+ return false;
+ }
+
+ if (!_engine->getResourceManager()->loadArchive((ArchiveIndex)index))
+ return false;
+
+ getScenes()->loadSceneDataFile((ArchiveIndex)index);
+
+ return true;
}
// Restore loaded archive
@@ -233,8 +244,10 @@ bool Debugger::cmdListFiles(int argc, const char **argv) {
Common::String filter(const_cast<char *>(argv[1]));
// Load the proper archive
- if (argc == 3)
- loadArchive((ArchiveIndex)getNumber(argv[2]));
+ if (argc == 3) {
+ if (!loadArchive(getNumber(argv[2])))
+ return true;
+ }
Common::ArchiveMemberList list;
int count = _engine->getResourceManager()->listMatchingMembers(list, filter);
@@ -317,8 +330,10 @@ bool Debugger::cmdShowFrame(int argc, const char **argv) {
Common::String filename(const_cast<char *>(argv[1]));
filename += ".seq";
- if (argc == 4)
- loadArchive((ArchiveIndex)getNumber(argv[3]));
+ if (argc == 4) {
+ if (!loadArchive(getNumber(argv[3])))
+ return true;
+ }
if (!_engine->getResourceManager()->hasFile(filename)) {
DebugPrintf("Cannot find file: %s\n", filename.c_str());
@@ -377,8 +392,10 @@ bool Debugger::cmdShowBg(int argc, const char **argv) {
if (argc == 2 || argc == 3) {
Common::String filename(const_cast<char *>(argv[1]));
- if (argc == 3)
- loadArchive((ArchiveIndex)getNumber(argv[2]));
+ if (argc == 3) {
+ if (!loadArchive(getNumber(argv[2])))
+ return true;
+ }
if (!_engine->getResourceManager()->hasFile(filename + ".BG")) {
DebugPrintf("Cannot find file: %s\n", (filename + ".BG").c_str());
@@ -430,8 +447,10 @@ bool Debugger::cmdPlaySeq(int argc, const char **argv) {
Common::String filename(const_cast<char *>(argv[1]));
filename += ".seq";
- if (argc == 3)
- loadArchive((ArchiveIndex)getNumber(argv[2]));
+ if (argc == 3) {
+ if (!loadArchive(getNumber(argv[2])))
+ return true;
+ }
if (!_engine->getResourceManager()->hasFile(filename)) {
DebugPrintf("Cannot find file: %s\n", filename.c_str());
@@ -505,8 +524,10 @@ bool Debugger::cmdPlaySeq(int argc, const char **argv) {
bool Debugger::cmdPlaySnd(int argc, const char **argv) {
if (argc == 2 || argc == 3) {
- if (argc == 3)
- loadArchive((ArchiveIndex)getNumber(argv[2]));
+ if (argc == 3) {
+ if (!loadArchive(getNumber(argv[2])))
+ return true;
+ }
// Add .SND at the end of the filename if needed
Common::String name(const_cast<char *>(argv[1]));
@@ -520,7 +541,7 @@ bool Debugger::cmdPlaySnd(int argc, const char **argv) {
_engine->_system->getMixer()->stopAll();
- _soundStream->load(getArchive(name));
+ _soundStream->load(getArchive(name), 16);
if (argc == 3)
restoreArchive();
@@ -542,8 +563,10 @@ bool Debugger::cmdPlaySbe(int argc, const char **argv) {
if (argc == 2 || argc == 3) {
Common::String filename(const_cast<char *>(argv[1]));
- if (argc == 3)
- loadArchive((ArchiveIndex)getNumber(argv[2]));
+ if (argc == 3) {
+ if (!loadArchive(getNumber(argv[2])))
+ return true;
+ }
filename += ".sbe";
@@ -605,8 +628,10 @@ bool Debugger::cmdPlayNis(int argc, const char **argv) {
if (argc == 2 || argc == 3) {
Common::String name(const_cast<char *>(argv[1]));
- if (argc == 3)
- loadArchive((ArchiveIndex)getNumber(argv[2]));
+ if (argc == 3) {
+ if (!loadArchive(getNumber(argv[2])))
+ return true;
+ }
// If we got a nis filename, check that the file exists
if (name.contains('.') && !_engine->getResourceManager()->hasFile(name)) {
@@ -662,8 +687,10 @@ bool Debugger::cmdLoadScene(int argc, const char **argv) {
SceneIndex index = (SceneIndex)getNumber(argv[1]);
// Check args
- if (argc == 3)
- loadArchive((ArchiveIndex)getNumber(argv[2]));
+ if (argc == 3) {
+ if (!loadArchive(getNumber(argv[2])))
+ return true;
+ }
if (index > 2500) {
DebugPrintf("Error: invalid index value (0-2500)");
@@ -1091,30 +1118,6 @@ label_error:
}
/**
- * Command: loads a game
- *
- * @param argc The argument count.
- * @param argv The values.
- *
- * @return true if it was handled, false otherwise
- */
-bool Debugger::cmdLoadGame(int argc, const char **argv) {
- if (argc == 2) {
- int id = getNumber(argv[1]);
-
- if (id == 0 || id > 6)
- goto error;
-
- getSaveLoad()->loadGame((GameId)(id - 1));
- } else {
-error:
- DebugPrintf("Syntax: loadgame <id> (id=1-6)\n");
- }
-
- return true;
-}
-
-/**
* Command: switch to a specific chapter
*
* @param argc The argument count.
diff --git a/engines/lastexpress/debug.h b/engines/lastexpress/debug.h
index d9ba6f47a1..532cb83717 100644
--- a/engines/lastexpress/debug.h
+++ b/engines/lastexpress/debug.h
@@ -79,7 +79,6 @@ private:
bool cmdShow(int argc, const char **argv);
bool cmdEntity(int argc, const char **argv);
- bool cmdLoadGame(int argc, const char **argv);
bool cmdSwitchChapter(int argc, const char **argv);
bool cmdClear(int argc, const char **argv);
@@ -87,7 +86,7 @@ private:
void copyCommand(int argc, const char **argv);
int getNumber(const char *arg) const;
- void loadArchive(ArchiveIndex index) const;
+ bool loadArchive(int index);
void restoreArchive() const;
Debuglet *_command;
diff --git a/engines/lastexpress/entities/abbot.cpp b/engines/lastexpress/entities/abbot.cpp
index e0fe429520..406b017d3a 100644
--- a/engines/lastexpress/entities/abbot.cpp
+++ b/engines/lastexpress/entities/abbot.cpp
@@ -58,10 +58,10 @@ Abbot::Abbot(LastExpressEngine *engine) : Entity(engine, kEntityAbbot) {
ADD_CALLBACK_FUNCTION(Abbot, chapter2);
ADD_CALLBACK_FUNCTION(Abbot, chapter3);
ADD_CALLBACK_FUNCTION(Abbot, chapter3Handler);
- ADD_CALLBACK_FUNCTION(Abbot, function19);
- ADD_CALLBACK_FUNCTION(Abbot, function20);
- ADD_CALLBACK_FUNCTION(Abbot, function21);
- ADD_CALLBACK_FUNCTION(Abbot, function22);
+ ADD_CALLBACK_FUNCTION(Abbot, conversationWithBoutarel);
+ ADD_CALLBACK_FUNCTION(Abbot, readPaper);
+ ADD_CALLBACK_FUNCTION(Abbot, goToLunch);
+ ADD_CALLBACK_FUNCTION(Abbot, haveLunch);
ADD_CALLBACK_FUNCTION(Abbot, function23);
ADD_CALLBACK_FUNCTION(Abbot, function24);
ADD_CALLBACK_FUNCTION(Abbot, function25);
@@ -259,7 +259,7 @@ IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler)
getData()->entityPosition = kPosition_6470;
getData()->location = kLocationInsideCompartment;
- setup_function19();
+ setup_conversationWithBoutarel();
break;
}
break;
@@ -272,7 +272,7 @@ IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(19, Abbot, function19)
+IMPLEMENT_FUNCTION(19, Abbot, conversationWithBoutarel)
switch (savepoint.action) {
default:
break;
@@ -311,21 +311,21 @@ IMPLEMENT_FUNCTION(19, Abbot, function19)
case 3:
getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
- setup_function20();
+ setup_readPaper();
break;
}
}
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(20, Abbot, function20)
+IMPLEMENT_FUNCTION(20, Abbot, readPaper)
switch (savepoint.action) {
default:
break;
case kActionNone:
if (getState()->time > kTime1966500 && getEntities()->isInRestaurant(kEntityBoutarel))
- setup_function21();
+ setup_goToLunch();
break;
case kActionDefault:
@@ -335,7 +335,7 @@ IMPLEMENT_FUNCTION(20, Abbot, function20)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(21, Abbot, function21)
+IMPLEMENT_FUNCTION(21, Abbot, goToLunch)
switch (savepoint.action) {
default:
break;
@@ -393,7 +393,7 @@ IMPLEMENT_FUNCTION(21, Abbot, function21)
break;
case 7:
- setup_function22();
+ setup_haveLunch();
break;
}
break;
@@ -409,7 +409,7 @@ IMPLEMENT_FUNCTION(21, Abbot, function21)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(22, Abbot, function22)
+IMPLEMENT_FUNCTION(22, Abbot, haveLunch)
switch (savepoint.action) {
default:
break;
@@ -1547,7 +1547,7 @@ IMPLEMENT_FUNCTION(45, Abbot, function45)
getData()->car = kCarRedSleeping;
getData()->location = kLocationOutsideCompartment;
- RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function38);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_resetState);
getEntities()->drawSequenceLeft(kEntityAbbot, "617Ec");
getEntities()->enterCompartment(kEntityAbbot, kObjectCompartmentC, true);
diff --git a/engines/lastexpress/entities/abbot.h b/engines/lastexpress/entities/abbot.h
index ce52bb68ce..dc3e86db54 100644
--- a/engines/lastexpress/entities/abbot.h
+++ b/engines/lastexpress/entities/abbot.h
@@ -156,10 +156,10 @@ public:
* Handle Chapter 3 events
*/
DECLARE_FUNCTION(chapter3Handler)
- DECLARE_FUNCTION(function19)
- DECLARE_FUNCTION(function20)
- DECLARE_FUNCTION(function21)
- DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(conversationWithBoutarel)
+ DECLARE_FUNCTION(readPaper)
+ DECLARE_FUNCTION(goToLunch)
+ DECLARE_FUNCTION(haveLunch)
DECLARE_FUNCTION(function23)
DECLARE_FUNCTION(function24)
DECLARE_FUNCTION(function25)
diff --git a/engines/lastexpress/entities/august.cpp b/engines/lastexpress/entities/august.cpp
index 67d810fde2..dbae7bad20 100644
--- a/engines/lastexpress/entities/august.cpp
+++ b/engines/lastexpress/entities/august.cpp
@@ -3530,7 +3530,7 @@ IMPLEMENT_FUNCTION(69, August, unhookCars)
getScenes()->loadSceneFromPosition(kCarRestaurant, 85, 1);
getSavePoints()->pushAll(kEntityAugust, kActionProceedChapter5);
- RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function42)
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_end)
}
break;
}
diff --git a/engines/lastexpress/entities/chapters.cpp b/engines/lastexpress/entities/chapters.cpp
index a2f3a3d871..d373432710 100644
--- a/engines/lastexpress/entities/chapters.cpp
+++ b/engines/lastexpress/entities/chapters.cpp
@@ -1851,7 +1851,7 @@ void Chapters::enterExitHelper(bool isEnteringStation) {
callbackAction();
}
-void Chapters::playSteam() {
+void Chapters::playSteam() const {
getSoundQueue()->resetState();
getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4));
ENTITY_PARAM(0, 2) = 0;
diff --git a/engines/lastexpress/entities/chapters.h b/engines/lastexpress/entities/chapters.h
index ddb3de3bea..fb52ea3ee4 100644
--- a/engines/lastexpress/entities/chapters.h
+++ b/engines/lastexpress/entities/chapters.h
@@ -157,7 +157,7 @@ private:
bool timeCheckExitStation(TimeValue timeValue, uint &parameter, byte callback, const char *sequence);
void enterExitStation(const SavePoint &savepoint, bool isEnteringStation);
void enterExitHelper(bool isEnteringStation);
- void playSteam();
+ void playSteam() const;
};
} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/entity.cpp b/engines/lastexpress/entities/entity.cpp
index 2deca291f6..dad5e67392 100644
--- a/engines/lastexpress/entities/entity.cpp
+++ b/engines/lastexpress/entities/entity.cpp
@@ -26,10 +26,15 @@
#include "lastexpress/game/action.h"
#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
#include "lastexpress/game/object.h"
#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
#include "lastexpress/game/scenes.h"
+#include "lastexpress/lastexpress.h"
+
namespace LastExpress {
//////////////////////////////////////////////////////////////////////////
@@ -45,12 +50,14 @@ EntityData::EntityCallData::~EntityCallData() {
SAFE_DELETE(sequence3);
}
-void EntityData::EntityCallData::syncString(Common::Serializer &s, Common::String &string, int length) {
+void EntityData::EntityCallData::syncString(Common::Serializer &s, Common::String &string, uint length) const {
char seqName[13];
memset(&seqName, 0, length);
- if (s.isSaving()) strcpy((char *)&seqName, string.c_str());
- s.syncBytes((byte *)&seqName, length);
+ if (s.isSaving())
+ strcpy((char *)&seqName, string.c_str());
+
+ s.syncBytes((byte *)&seqName, length);
if (s.isLoading())
string = seqName;
@@ -110,7 +117,7 @@ EntityData::EntityParameters *EntityData::getParameters(uint callback, byte inde
return _parameters[callback].parameters[index];
}
-int EntityData::getCallback(uint callback) const {
+byte EntityData::getCallback(uint callback) const {
if (callback >= 16)
error("[EntityData::getCallback] Invalid callback value (was: %d, max: 16)", callback);
@@ -818,7 +825,7 @@ void Entity::setupIISS(const char *name, uint index, uint param1, uint param2, c
// Helper functions
//////////////////////////////////////////////////////////////////////////
-bool Entity::updateParameter(uint &parameter, uint timeType, uint delta) {
+bool Entity::updateParameter(uint &parameter, uint timeType, uint delta) const {
if (!parameter)
parameter = (uint)(timeType + delta);
@@ -830,7 +837,7 @@ bool Entity::updateParameter(uint &parameter, uint timeType, uint delta) {
return true;
}
-bool Entity::updateParameterTime(TimeValue timeValue, bool check, uint &parameter, uint delta) {
+bool Entity::updateParameterTime(TimeValue timeValue, bool check, uint &parameter, uint delta) const {
if (getState()->time <= timeValue) {
if (check || !parameter)
parameter = (uint)(getState()->time + delta);
@@ -844,7 +851,7 @@ bool Entity::updateParameterTime(TimeValue timeValue, bool check, uint &paramete
return true;
}
-bool Entity::updateParameterCheck(uint &parameter, uint timeType, uint delta) {
+bool Entity::updateParameterCheck(uint &parameter, uint timeType, uint delta) const {
if (parameter && parameter >= timeType)
return false;
@@ -854,7 +861,7 @@ bool Entity::updateParameterCheck(uint &parameter, uint timeType, uint delta) {
return true;
}
-bool Entity::timeCheck(TimeValue timeValue, uint &parameter, Common::Functor0<void> *function) {
+bool Entity::timeCheck(TimeValue timeValue, uint &parameter, Common::Functor0<void> *function) const {
if (getState()->time > timeValue && !parameter) {
parameter = 1;
(*function)();
@@ -929,14 +936,14 @@ bool Entity::timeCheckCar(TimeValue timeValue, uint &parameter, byte callback, C
return false;
}
-void Entity::timeCheckSavepoint(TimeValue timeValue, uint &parameter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) {
+void Entity::timeCheckSavepoint(TimeValue timeValue, uint &parameter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) const {
if (getState()->time > timeValue && !parameter) {
parameter = 1;
getSavePoints()->push(entity1, entity2, action);
}
}
-void Entity::timeCheckObject(TimeValue timeValue, uint &parameter, ObjectIndex object, ObjectLocation location) {
+void Entity::timeCheckObject(TimeValue timeValue, uint &parameter, ObjectIndex object, ObjectLocation location) const {
if (getState()->time > timeValue && !parameter) {
parameter = 1;
getObjects()->updateLocation2(object, location);
diff --git a/engines/lastexpress/entities/entity.h b/engines/lastexpress/entities/entity.h
index 3601f34f6f..c67d13db9e 100644
--- a/engines/lastexpress/entities/entity.h
+++ b/engines/lastexpress/entities/entity.h
@@ -25,13 +25,8 @@
#include "lastexpress/shared.h"
-#include "lastexpress/game/logic.h"
-#include "lastexpress/game/savepoint.h"
-#include "lastexpress/game/state.h"
-
#include "lastexpress/sound/sound.h"
-#include "lastexpress/lastexpress.h"
#include "lastexpress/helpers.h"
#include "common/array.h"
@@ -827,7 +822,7 @@ public:
* @param string The string.
* @param length Length of the string.
*/
- void syncString(Common::Serializer &s, Common::String &string, int length);
+ void syncString(Common::Serializer &s, Common::String &string, uint length) const;
// Serializable
void saveLoadWithSerializer(Common::Serializer &s);
@@ -850,8 +845,8 @@ public:
EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); }
EntityCallParameters *getCurrentCallParameters() { return &_parameters[_data.currentCall]; }
- int getCallback(uint callback) const;
- int getCurrentCallback() { return getCallback(_data.currentCall); }
+ byte getCallback(uint callback) const;
+ byte getCurrentCallback() { return getCallback(_data.currentCall); }
void setCallback(uint callback, byte index);
void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); }
@@ -1089,18 +1084,18 @@ protected:
// Helper functions
//////////////////////////////////////////////////////////////////////////
- bool updateParameter(uint &parameter, uint timeType, uint delta);
- bool updateParameterCheck(uint &parameter, uint timeType, uint delta);
- bool updateParameterTime(TimeValue timeValue, bool check, uint &parameter, uint delta);
+ bool updateParameter(uint &parameter, uint timeType, uint delta) const;
+ bool updateParameterCheck(uint &parameter, uint timeType, uint delta) const;
+ bool updateParameterTime(TimeValue timeValue, bool check, uint &parameter, uint delta) const;
- bool timeCheck(TimeValue timeValue, uint &parameter, Common::Functor0<void> *function);
+ bool timeCheck(TimeValue timeValue, uint &parameter, Common::Functor0<void> *function) const;
bool timeCheckCallback(TimeValue timeValue, uint &parameter, byte callback, Common::Functor0<void> *function);
bool timeCheckCallback(TimeValue timeValue, uint &parameter, byte callback, const char *str, Common::Functor1<const char *, void> *function);
bool timeCheckCallback(TimeValue timeValue, uint &parameter, byte callback, bool check, Common::Functor1<bool, void> *function);
bool timeCheckCallbackInventory(TimeValue timeValue, uint &parameter, byte callback, Common::Functor0<void> *function);
bool timeCheckCar(TimeValue timeValue, uint &parameter, byte callback, Common::Functor0<void> *function);
- void timeCheckSavepoint(TimeValue timeValue, uint &parameter, EntityIndex entity1, EntityIndex entity2, ActionIndex action);
- void timeCheckObject(TimeValue timeValue, uint &parameter, ObjectIndex index, ObjectLocation location);
+ void timeCheckSavepoint(TimeValue timeValue, uint &parameter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) const;
+ void timeCheckObject(TimeValue timeValue, uint &parameter, ObjectIndex index, ObjectLocation location) const;
bool timeCheckCallbackAction(TimeValue timeValue, uint &parameter);
bool timeCheckPlaySoundUpdatePosition(TimeValue timeValue, uint &parameter, byte callback, const char* sound, EntityPosition position);
diff --git a/engines/lastexpress/entities/verges.cpp b/engines/lastexpress/entities/verges.cpp
index 867f122d8f..d9ddb0a4d1 100644
--- a/engines/lastexpress/entities/verges.cpp
+++ b/engines/lastexpress/entities/verges.cpp
@@ -46,40 +46,40 @@ Verges::Verges(LastExpressEngine *engine) : Entity(engine, kEntityVerges) {
ADD_CALLBACK_FUNCTION(Verges, callbackActionRestaurantOrSalon);
ADD_CALLBACK_FUNCTION(Verges, savegame);
ADD_CALLBACK_FUNCTION(Verges, updateEntity);
- ADD_CALLBACK_FUNCTION(Verges, function9);
- ADD_CALLBACK_FUNCTION(Verges, function10);
+ ADD_CALLBACK_FUNCTION(Verges, walkBetweenCars);
+ ADD_CALLBACK_FUNCTION(Verges, makeAnnouncement);
ADD_CALLBACK_FUNCTION(Verges, function11);
ADD_CALLBACK_FUNCTION(Verges, function12);
- ADD_CALLBACK_FUNCTION(Verges, function13);
+ ADD_CALLBACK_FUNCTION(Verges, baggageCar);
ADD_CALLBACK_FUNCTION(Verges, updateFromTime);
- ADD_CALLBACK_FUNCTION(Verges, function15);
- ADD_CALLBACK_FUNCTION(Verges, function16);
- ADD_CALLBACK_FUNCTION(Verges, function17);
+ ADD_CALLBACK_FUNCTION(Verges, dialog);
+ ADD_CALLBACK_FUNCTION(Verges, dialog2);
+ ADD_CALLBACK_FUNCTION(Verges, talkAboutPassengerList);
ADD_CALLBACK_FUNCTION(Verges, chapter1);
ADD_CALLBACK_FUNCTION(Verges, talkHarem);
ADD_CALLBACK_FUNCTION(Verges, talkPassengerList);
ADD_CALLBACK_FUNCTION(Verges, talkGendarmes);
- ADD_CALLBACK_FUNCTION(Verges, function22);
+ ADD_CALLBACK_FUNCTION(Verges, askMertensToRelayAugustInvitation);
ADD_CALLBACK_FUNCTION(Verges, function23);
ADD_CALLBACK_FUNCTION(Verges, policeGettingOffTrain);
- ADD_CALLBACK_FUNCTION(Verges, function25);
+ ADD_CALLBACK_FUNCTION(Verges, policeSearch);
ADD_CALLBACK_FUNCTION(Verges, chapter1Handler);
ADD_CALLBACK_FUNCTION(Verges, chapter2);
ADD_CALLBACK_FUNCTION(Verges, chapter2Handler);
ADD_CALLBACK_FUNCTION(Verges, chapter3);
ADD_CALLBACK_FUNCTION(Verges, function30);
- ADD_CALLBACK_FUNCTION(Verges, function31);
+ ADD_CALLBACK_FUNCTION(Verges, talkAboutMax);
ADD_CALLBACK_FUNCTION(Verges, function32);
ADD_CALLBACK_FUNCTION(Verges, function33);
ADD_CALLBACK_FUNCTION(Verges, function34);
- ADD_CALLBACK_FUNCTION(Verges, function35);
+ ADD_CALLBACK_FUNCTION(Verges, organizeConcertInvitations);
ADD_CALLBACK_FUNCTION(Verges, chapter4);
ADD_CALLBACK_FUNCTION(Verges, chapter4Handler);
- ADD_CALLBACK_FUNCTION(Verges, function38);
+ ADD_CALLBACK_FUNCTION(Verges, resetState);
ADD_CALLBACK_FUNCTION(Verges, chapter5);
ADD_CALLBACK_FUNCTION(Verges, chapter5Handler);
- ADD_CALLBACK_FUNCTION(Verges, function41);
- ADD_CALLBACK_FUNCTION(Verges, function42);
+ ADD_CALLBACK_FUNCTION(Verges, askPassengersToStayInCompartments);
+ ADD_CALLBACK_FUNCTION(Verges, end);
}
//////////////////////////////////////////////////////////////////////////
@@ -149,7 +149,7 @@ IMPLEMENT_FUNCTION_II(8, Verges, updateEntity, CarIndex, EntityPosition)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION_S(9, Verges, function9)
+IMPLEMENT_FUNCTION_S(9, Verges, walkBetweenCars)
switch (savepoint.action) {
default:
break;
@@ -201,7 +201,7 @@ switch (savepoint.action) {
case 3:
setCallback(4);
- setup_function10(kCarGreenSleeping, kPosition_540, (char *)&params->seq1);
+ setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, (char *)&params->seq1);
break;
case 4:
@@ -225,7 +225,7 @@ switch (savepoint.action) {
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition)
+IMPLEMENT_FUNCTION_IIS(10, Verges, makeAnnouncement, CarIndex, EntityPosition)
switch (savepoint.action) {
default:
break;
@@ -404,7 +404,7 @@ IMPLEMENT_FUNCTION(12, Verges, function12)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION_I(13, Verges, function13, bool)
+IMPLEMENT_FUNCTION_I(13, Verges, baggageCar, bool)
switch (savepoint.action) {
default:
break;
@@ -449,7 +449,7 @@ IMPLEMENT_FUNCTION_I(14, Verges, updateFromTime, uint32)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex)
+IMPLEMENT_FUNCTION_IS(15, Verges, dialog, EntityIndex)
switch (savepoint.action) {
default:
break;
@@ -486,7 +486,7 @@ IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex)
+IMPLEMENT_FUNCTION_ISS(16, Verges, dialog2, EntityIndex)
switch (savepoint.action) {
default:
break;
@@ -526,7 +526,7 @@ IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(17, Verges, function17)
+IMPLEMENT_FUNCTION(17, Verges, talkAboutPassengerList)
switch (savepoint.action) {
default:
break;
@@ -548,7 +548,7 @@ IMPLEMENT_FUNCTION(17, Verges, function17)
case 2:
setCallback(3);
- setup_function15(kEntityMertens, "TRA1291");
+ setup_dialog(kEntityMertens, "TRA1291");
break;
case 3:
@@ -611,7 +611,7 @@ IMPLEMENT_FUNCTION(21, Verges, talkGendarmes)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(22, Verges, function22)
+IMPLEMENT_FUNCTION(22, Verges, askMertensToRelayAugustInvitation)
switch (savepoint.action) {
default:
break;
@@ -634,10 +634,10 @@ IMPLEMENT_FUNCTION(22, Verges, function22)
case 2:
if (getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAugustWaiting)) {
setCallback(3);
- setup_function16(kEntityMertens, "TRA1200", "TRA1201");
+ setup_dialog2(kEntityMertens, "TRA1200", "TRA1201");
} else {
setCallback(4);
- setup_function16(kEntityMertens, "TRA1200A", "TRA1201");
+ setup_dialog2(kEntityMertens, "TRA1200A", "TRA1201");
}
break;
@@ -714,7 +714,7 @@ IMPLEMENT_FUNCTION(24, Verges, policeGettingOffTrain)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(25, Verges, function25)
+IMPLEMENT_FUNCTION(25, Verges, policeSearch)
switch (savepoint.action) {
default:
break;
@@ -774,10 +774,10 @@ IMPLEMENT_FUNCTION(25, Verges, function25)
if (getData()->car == kCarRedSleeping) {
setCallback(6);
- setup_function10(kCarGreenSleeping, kPosition_540, "TRA1005");
+ setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA1005");
} else {
setCallback(7);
- setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006");
+ setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA1006");
}
break;
}
@@ -805,7 +805,7 @@ IMPLEMENT_FUNCTION(25, Verges, function25)
getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872);
setCallback(4);
- setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006");
+ setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA1006");
break;
case 4:
@@ -838,7 +838,7 @@ IMPLEMENT_FUNCTION(25, Verges, function25)
getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872);
setCallback(10);
- setup_function10(kCarGreenSleeping, kPosition_540, "TRA1006");
+ setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA1006");
break;
case 10:
@@ -892,14 +892,14 @@ IMPLEMENT_FUNCTION(26, Verges, chapter1Handler)
label_callback1:
if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
setCallback(2);
- setup_function13(false);
+ setup_baggageCar(false);
break;
}
label_callback2:
if (ENTITY_PARAM(0, 7)) {
setCallback(3);
- setup_function25();
+ setup_policeSearch();
break;
}
@@ -907,7 +907,7 @@ label_callback3:
if (params->param6)
goto label_callback12;
- if (Entity::timeCheckCallback(kTimeChapter1, params->param7, 4, "TRA1001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTimeChapter1, params->param7, 4, "TRA1001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback4:
@@ -923,19 +923,19 @@ label_callback4:
}
label_callback8:
- if (Entity::timeCheckCallback(kTime1107000, CURRENT_PARAM(1, 1), 9, "TRA1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime1107000, CURRENT_PARAM(1, 1), 9, "TRA1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback9:
- if (Entity::timeCheckCallback(kTime1134000, CURRENT_PARAM(1, 2), 10, "TRA1002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime1134000, CURRENT_PARAM(1, 2), 10, "TRA1002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback10:
- if (Entity::timeCheckCallback(kTime1165500, CURRENT_PARAM(1, 3), 11, "TRA1003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime1165500, CURRENT_PARAM(1, 3), 11, "TRA1003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback11:
- if (Entity::timeCheckCallback(kTime1225800, CURRENT_PARAM(1, 4), 12, "TRA1004", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime1225800, CURRENT_PARAM(1, 4), 12, "TRA1004", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback12:
@@ -955,7 +955,7 @@ label_callback13:
label_callback14:
if (ENTITY_PARAM(0, 3) && !params->param4 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) {
setCallback(15);
- setup_function17();
+ setup_talkAboutPassengerList();
break;
}
@@ -963,14 +963,14 @@ label_callback15:
if (ENTITY_PARAM(0, 1) && !params->param5) {
if (getState()->time < kTime1134000 || getState()->time > kTime1156500) {
setCallback(16);
- setup_function22();
+ setup_askMertensToRelayAugustInvitation();
}
}
break;
case kActionOpenDoor:
setCallback(17);
- setup_function13(savepoint.param.intValue < 106 ? true : false);
+ setup_baggageCar(savepoint.param.intValue < 106 ? true : false);
break;
case kActionDefault:
@@ -1006,7 +1006,7 @@ label_callback15:
case 6:
setCallback(7);
- setup_function15(kEntityMertens, "TRA1202");
+ setup_dialog(kEntityMertens, "TRA1202");
break;
case 7:
@@ -1084,11 +1084,11 @@ IMPLEMENT_FUNCTION(28, Verges, chapter2Handler)
case kActionNone:
if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
setCallback(1);
- setup_function13(false);
+ setup_baggageCar(false);
}
label_callback_1:
- if (Entity::timeCheckCallback(kTime1818900, params->param1, 2, "Tra2177", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime1818900, params->param1, 2, "Tra2177", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_2:
@@ -1117,7 +1117,7 @@ label_callback_6:
if (ENTITY_PARAM(0, 3)) {
setCallback(7);
- setup_function17();
+ setup_talkAboutPassengerList();
}
break;
@@ -1130,7 +1130,7 @@ label_callback_6:
case kActionOpenDoor:
setCallback(8);
- setup_function13(savepoint.param.intValue < 106);
+ setup_baggageCar(savepoint.param.intValue < 106);
break;
case kActionDefault:
@@ -1155,7 +1155,7 @@ label_callback_6:
case 4:
setCallback(5);
- setup_function15(kEntityCoudert, "TRA2100");
+ setup_dialog(kEntityCoudert, "TRA2100");
break;
case 5:
@@ -1177,7 +1177,7 @@ IMPLEMENT_FUNCTION(29, Verges, chapter3)
break;
case kActionNone:
- setup_function23();
+ setup_function33();
break;
case kActionDefault:
@@ -1221,7 +1221,7 @@ IMPLEMENT_FUNCTION_S(30, Verges, function30)
case 2:
setCallback(3);
- setup_function15(kEntityCoudert, (char *)&params->seq1);
+ setup_dialog(kEntityCoudert, (char *)&params->seq1);
break;
case 3:
@@ -1238,7 +1238,7 @@ IMPLEMENT_FUNCTION_S(30, Verges, function30)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(31, Verges, function31)
+IMPLEMENT_FUNCTION(31, Verges, talkAboutMax)
switch (savepoint.action) {
default:
break;
@@ -1260,7 +1260,7 @@ IMPLEMENT_FUNCTION(31, Verges, function31)
case 2:
setCallback(3);
- setup_function15(kEntityCoudert, "TRA3015");
+ setup_dialog(kEntityCoudert, "TRA3015");
break;
case 3:
@@ -1289,7 +1289,7 @@ IMPLEMENT_FUNCTION(32, Verges, function32)
if (getState()->time > kTime2263500 && !params->param1) {
params->param1 = 1;
setCallback(5);
- setup_function10(kCarRedSleeping, kPosition_9460, "TRA3006");
+ setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA3006");
break;
}
break;
@@ -1341,7 +1341,7 @@ IMPLEMENT_FUNCTION(32, Verges, function32)
case 3:
setCallback(4);
- setup_function10(kCarGreenSleeping, kPosition_540, "TRA3004");
+ setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA3004");
break;
case 4:
@@ -1412,7 +1412,7 @@ IMPLEMENT_FUNCTION(33, Verges, function33)
getSavePoints()->push(kEntityVerges, kEntityAbbot, kAction192054567);
setCallback(6);
- setup_function9("Tra3010");
+ setup_walkBetweenCars("Tra3010");
break;
case 6:
@@ -1432,42 +1432,42 @@ IMPLEMENT_FUNCTION(34, Verges, function34)
case kActionNone:
if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
setCallback(1);
- setup_function13(false);
+ setup_baggageCar(false);
break;
}
label_callback_1:
if (ENTITY_PARAM(0, 4)) {
setCallback(2);
- setup_function31();
+ setup_talkAboutMax();
break;
}
label_callback_2:
if (ENTITY_PARAM(0, 3)) {
setCallback(3);
- setup_function17();
+ setup_talkAboutPassengerList();
break;
}
label_callback_3:
- if (Entity::timeCheckCallback(kTime1971000, params->param1, 4, "Tra3001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime1971000, params->param1, 4, "Tra3001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_4:
- if (Entity::timeCheckCallback(kTime1998000, params->param2, 5, "Tra3010a", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime1998000, params->param2, 5, "Tra3010a", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_5:
- if (Entity::timeCheckCallback(kTime2016000, params->param3, 6, WRAP_SETUP_FUNCTION(Verges, setup_function35)))
+ if (Entity::timeCheckCallback(kTime2016000, params->param3, 6, WRAP_SETUP_FUNCTION(Verges, setup_organizeConcertInvitations)))
break;
label_callback_6:
- if (Entity::timeCheckCallback(kTime2070000, params->param4, 7, "Tra3002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime2070000, params->param4, 7, "Tra3002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_7:
- if (Entity::timeCheckCallback(kTime2142000, params->param5, 8, "Tra3003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime2142000, params->param5, 8, "Tra3003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_8:
@@ -1480,7 +1480,7 @@ label_callback_9:
case kActionOpenDoor:
setCallback(11);
- setup_function13(savepoint.param.intValue < 106);
+ setup_baggageCar(savepoint.param.intValue < 106);
break;
case kActionCallback:
@@ -1520,7 +1520,7 @@ label_callback_9:
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(35, Verges, function35)
+IMPLEMENT_FUNCTION(35, Verges, organizeConcertInvitations)
switch (savepoint.action) {
default:
break;
@@ -1542,7 +1542,7 @@ IMPLEMENT_FUNCTION(35, Verges, function35)
case 2:
setCallback(3);
- setup_function15(kEntityMertens, "Tra3011A");
+ setup_dialog(kEntityMertens, "Tra3011A");
break;
case 3:
@@ -1554,7 +1554,7 @@ IMPLEMENT_FUNCTION(35, Verges, function35)
case 4:
setCallback(5);
- setup_function15(kEntityMertens, "Tra3011");
+ setup_dialog(kEntityMertens, "Tra3011");
break;
case 5:
@@ -1609,7 +1609,7 @@ IMPLEMENT_FUNCTION(37, Verges, chapter4Handler)
case kActionNone:
if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
setCallback(1);
- setup_function13(false);
+ setup_baggageCar(false);
break;
}
@@ -1617,42 +1617,42 @@ label_callback_1:
if (ENTITY_PARAM(0, 6)) {
if (ENTITY_PARAM(0, 3)) {
setCallback(2);
- setup_function17();
+ setup_talkAboutPassengerList();
break;
}
label_callback_2:
- if (Entity::timeCheckCallback(kTime2349000, params->param1, 3, "Tra1001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime2349000, params->param1, 3, "Tra1001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_3:
- if (Entity::timeCheckCallback(kTime2378700, params->param2, 4, "Tra4001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime2378700, params->param2, 4, "Tra4001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_4:
- if (Entity::timeCheckCallback(kTime2403000, params->param3, 5, "Tra1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime2403000, params->param3, 5, "Tra1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_5:
- if (Entity::timeCheckCallback(kTime2414700, params->param4, 6, "Tra4002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime2414700, params->param4, 6, "Tra4002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_6:
- if (Entity::timeCheckCallback(kTime2484000, params->param5, 7, "Tra4003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime2484000, params->param5, 7, "Tra4003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
label_callback_7:
- if (Entity::timeCheckCallback(kTime2511000, params->param6, 8, "Tra4004", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)))
+ if (Entity::timeCheckCallback(kTime2511000, params->param6, 8, "Tra4004", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)))
break;
}
label_callback_8:
- Entity::timeCheckCallback(kTime2538000, params->param7, 9, "Tra4005", WRAP_SETUP_FUNCTION_S(Verges, setup_function9));
+ Entity::timeCheckCallback(kTime2538000, params->param7, 9, "Tra4005", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars));
break;
case kActionOpenDoor:
setCallback(10);
- setup_function13(savepoint.param.intValue < 106);
+ setup_baggageCar(savepoint.param.intValue < 106);
break;
case kActionDefault:
@@ -1697,7 +1697,7 @@ label_callback_8:
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(38, Verges, function38)
+IMPLEMENT_FUNCTION(38, Verges, resetState)
switch (savepoint.action) {
default:
break;
@@ -1803,14 +1803,14 @@ IMPLEMENT_FUNCTION(40, Verges, chapter5Handler)
getAction()->playAnimation(kEventCathFreePassengers);
getSavePoints()->pushAll(kEntityVerges, kActionProceedChapter5);
getScenes()->loadSceneFromPosition(kCarRedSleeping, 40);
- setup_function41();
+ setup_askPassengersToStayInCompartments();
}
break;
}
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(41, Verges, function41)
+IMPLEMENT_FUNCTION(41, Verges, askPassengersToStayInCompartments)
switch (savepoint.action) {
default:
break;
@@ -1822,7 +1822,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41)
getData()->location = kLocationInsideCompartment;
setCallback(1);
- setup_function10(kCarRedSleeping, kPosition_2000, "Tra5001");
+ setup_makeAnnouncement(kCarRedSleeping, kPosition_2000, "Tra5001");
break;
case kActionCallback:
@@ -1852,7 +1852,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41)
break;
case 4:
- setup_function42();
+ setup_end();
break;
}
break;
@@ -1860,7 +1860,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41)
IMPLEMENT_FUNCTION_END
//////////////////////////////////////////////////////////////////////////
-IMPLEMENT_FUNCTION(42, Verges, function42)
+IMPLEMENT_FUNCTION(42, Verges, end)
if (savepoint.action == kActionDefault)
getEntities()->clearSequences(kEntityVerges);
IMPLEMENT_FUNCTION_END
@@ -1891,7 +1891,7 @@ void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *so
case 2:
setCallback(3);
- setup_function15(kEntityCoudert, sound1);
+ setup_dialog(kEntityCoudert, sound1);
break;
case 3:
@@ -1901,7 +1901,7 @@ void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *so
case 4:
setCallback(5);
- setup_function15(kEntityMertens, sound2);
+ setup_dialog(kEntityMertens, sound2);
break;
case 5:
diff --git a/engines/lastexpress/entities/verges.h b/engines/lastexpress/entities/verges.h
index 82381043d3..93cc190d1e 100644
--- a/engines/lastexpress/entities/verges.h
+++ b/engines/lastexpress/entities/verges.h
@@ -87,11 +87,11 @@ public:
*/
DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
- DECLARE_FUNCTION_1(function9, const char *soundName)
- DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, const char *soundName)
+ DECLARE_FUNCTION_1(walkBetweenCars, const char *soundName)
+ DECLARE_FUNCTION_3(makeAnnouncement, CarIndex car, EntityPosition entityPosition, const char *soundName)
DECLARE_FUNCTION(function11)
DECLARE_FUNCTION(function12)
- DECLARE_FUNCTION_1(function13, bool)
+ DECLARE_FUNCTION_1(baggageCar, bool)
/**
* Updates parameter 2 using time value
@@ -100,9 +100,9 @@ public:
*/
DECLARE_FUNCTION_1(updateFromTime, uint32 time)
- DECLARE_FUNCTION_2(function15, EntityIndex entity, const char *soundName)
- DECLARE_FUNCTION_3(function16, EntityIndex entityIndex, const char *soundName1, const char *soundName2)
- DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION_2(dialog, EntityIndex entity, const char *soundName)
+ DECLARE_FUNCTION_3(dialog2, EntityIndex entityIndex, const char *soundName1, const char *soundName2)
+ DECLARE_FUNCTION(talkAboutPassengerList)
/**
* Setup Chapter 1
@@ -112,10 +112,10 @@ public:
DECLARE_FUNCTION_NOSETUP(talkHarem)
DECLARE_FUNCTION(talkPassengerList)
DECLARE_FUNCTION(talkGendarmes)
- DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(askMertensToRelayAugustInvitation)
DECLARE_FUNCTION(function23)
DECLARE_FUNCTION(policeGettingOffTrain)
- DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(policeSearch)
/**
* Handle Chapter 1 events
@@ -138,11 +138,11 @@ public:
DECLARE_FUNCTION(chapter3)
DECLARE_FUNCTION_1(function30, const char *soundName)
- DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(talkAboutMax)
DECLARE_FUNCTION(function32)
DECLARE_FUNCTION(function33)
DECLARE_FUNCTION(function34)
- DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(organizeConcertInvitations)
/**
* Setup Chapter 4
@@ -154,7 +154,7 @@ public:
*/
DECLARE_FUNCTION(chapter4Handler)
- DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(resetState)
/**
* Setup Chapter 5
@@ -166,8 +166,8 @@ public:
*/
DECLARE_FUNCTION(chapter5Handler)
- DECLARE_FUNCTION(function41)
- DECLARE_FUNCTION(function42)
+ DECLARE_FUNCTION(askPassengersToStayInCompartments)
+ DECLARE_FUNCTION(end)
private:
void talk(const SavePoint &savepoint, const char *sound1, const char *sound2);
diff --git a/engines/lastexpress/fight/fight.cpp b/engines/lastexpress/fight/fight.cpp
index b00c1732e7..49a9b85657 100644
--- a/engines/lastexpress/fight/fight.cpp
+++ b/engines/lastexpress/fight/fight.cpp
@@ -39,10 +39,8 @@
#include "lastexpress/game/state.h"
#include "lastexpress/sound/queue.h"
-#include "lastexpress/sound/sound.h"
#include "lastexpress/graphics.h"
-#include "lastexpress/helpers.h"
#include "lastexpress/lastexpress.h"
#include "lastexpress/resource.h"
diff --git a/engines/lastexpress/game/action.cpp b/engines/lastexpress/game/action.cpp
index 60a309518a..796abf2ce7 100644
--- a/engines/lastexpress/game/action.cpp
+++ b/engines/lastexpress/game/action.cpp
@@ -394,7 +394,7 @@ Action::Action(LastExpressEngine *engine) : _engine(engine) {
}
Action::~Action() {
- for (int i = 0; i < (int)_actions.size(); i++)
+ for (uint i = 0; i < _actions.size(); i++)
SAFE_DELETE(_actions[i]);
_actions.clear();
@@ -421,8 +421,6 @@ SceneIndex Action::processHotspot(const SceneHotspot &hotspot) {
// Action 0
IMPLEMENT_ACTION(dummy)
error("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action);
-
- return kSceneInvalid;
}
//////////////////////////////////////////////////////////////////////////
diff --git a/engines/lastexpress/game/beetle.cpp b/engines/lastexpress/game/beetle.cpp
index 2a72459697..d7a369ba40 100644
--- a/engines/lastexpress/game/beetle.cpp
+++ b/engines/lastexpress/game/beetle.cpp
@@ -94,7 +94,7 @@ void Beetle::load() {
// Check that all sequences are loaded properly
_data->isLoaded = true;
- for (int i = 0; i < (int)_data->sequences.size(); i++) {
+ for (uint i = 0; i < _data->sequences.size(); i++) {
if (!_data->sequences[i]->isLoaded()) {
_data->isLoaded = false;
break;
@@ -351,6 +351,9 @@ void Beetle::drawUpdate() {
}
void Beetle::invertDirection() {
+ if (!_data)
+ error("[Beetle::invertDirection] Sequences have not been loaded");
+
switch (_data->indexes[_data->offset]) {
default:
break;
diff --git a/engines/lastexpress/game/entities.cpp b/engines/lastexpress/game/entities.cpp
index 51db635bed..fafbd7cb64 100644
--- a/engines/lastexpress/game/entities.cpp
+++ b/engines/lastexpress/game/entities.cpp
@@ -181,7 +181,7 @@ Entities::Entities(LastExpressEngine *engine) : _engine(engine) {
Entities::~Entities() {
SAFE_DELETE(_header);
- for (int i = 0; i < (int)_entities.size(); i++)
+ for (uint i = 0; i < _entities.size(); i++)
SAFE_DELETE(_entities[i]);
_entities.clear();
@@ -669,7 +669,7 @@ void Entities::executeCallbacks() {
//////////////////////////////////////////////////////////////////////////
// Processing
//////////////////////////////////////////////////////////////////////////
-void Entities::incrementDirectionCounter(EntityData::EntityCallData *data) {
+void Entities::incrementDirectionCounter(EntityData::EntityCallData *data) const {
data->doProcessEntity = false;
if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight))
diff --git a/engines/lastexpress/game/entities.h b/engines/lastexpress/game/entities.h
index a9de7931f0..81aed627aa 100644
--- a/engines/lastexpress/game/entities.h
+++ b/engines/lastexpress/game/entities.h
@@ -344,7 +344,7 @@ private:
uint _positions[_positionsCount];
void executeCallbacks();
- void incrementDirectionCounter(EntityData::EntityCallData *data);
+ void incrementDirectionCounter(EntityData::EntityCallData *data) const;
void processEntity(EntityIndex entity);
void drawSequence(EntityIndex entity, const char *sequence, EntityDirection direction) const;
diff --git a/engines/lastexpress/game/inventory.cpp b/engines/lastexpress/game/inventory.cpp
index 52c00ece31..11e7369ee1 100644
--- a/engines/lastexpress/game/inventory.cpp
+++ b/engines/lastexpress/game/inventory.cpp
@@ -35,10 +35,8 @@
#include "lastexpress/menu/menu.h"
#include "lastexpress/sound/queue.h"
-#include "lastexpress/sound/sound.h"
#include "lastexpress/graphics.h"
-#include "lastexpress/helpers.h"
#include "lastexpress/lastexpress.h"
#include "lastexpress/resource.h"
@@ -621,7 +619,7 @@ void Inventory::drawEgg() const {
// Blinking egg: we need to blink the egg for delta time, with the blinking getting faster until it's always lit.
void Inventory::drawBlinkingEgg(uint ticks) {
- uint globalTimer = getGlobalTimer();
+ uint globalTimer = (uint)getGlobalTimer();
uint timerValue = (getProgress().jacket == kJacketGreen) ? 450 : 225;
if (globalTimer == timerValue || globalTimer == 900) {
@@ -655,7 +653,7 @@ void Inventory::drawBlinkingEgg(uint ticks) {
}
void Inventory::blinkEgg() {
- drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, (_blinkingBrightness == 0) ? -1 : _blinkingBrightness);
+ drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, (_blinkingBrightness == 0) ? -1 : (int16)_blinkingBrightness);
askForRedraw();
diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp
index 1696f100ff..09104d1bf9 100644
--- a/engines/lastexpress/game/logic.cpp
+++ b/engines/lastexpress/game/logic.cpp
@@ -48,7 +48,6 @@
#include "lastexpress/sound/queue.h"
-#include "lastexpress/graphics.h"
#include "lastexpress/lastexpress.h"
#include "lastexpress/resource.h"
diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp
index 360e99146a..021dc40bb9 100644
--- a/engines/lastexpress/game/savegame.cpp
+++ b/engines/lastexpress/game/savegame.cpp
@@ -78,7 +78,7 @@ uint32 SavegameStream::read(void *dataPtr, uint32 dataSize) {
uint32 SavegameStream::readUncompressed(void *dataPtr, uint32 dataSize) {
if ((int32)dataSize > size() - pos()) {
- dataSize = size() - pos();
+ dataSize = (uint32)(size() - pos());
_eos = true;
}
memcpy(dataPtr, getData() + pos(), dataSize);
@@ -230,7 +230,7 @@ uint32 SavegameStream::writeCompressed(const void *dataPtr, uint32 dataSize) {
if (*data != _previousValue || _repeatCount >= 255) {
if (_previousValue) {
writeBuffer(0xFF, true);
- writeBuffer(_repeatCount, true);
+ writeBuffer((uint8)_repeatCount, true);
writeBuffer(_previousValue, true);
_previousValue = *data++;
@@ -255,7 +255,7 @@ uint32 SavegameStream::writeCompressed(const void *dataPtr, uint32 dataSize) {
}
writeBuffer(0xFD, true);
- writeBuffer(_repeatCount, true);
+ writeBuffer((uint8)_repeatCount, true);
_previousValue = *data++;
_valueCount = 1;
@@ -348,11 +348,12 @@ uint32 SavegameStream::readCompressed(void *dataPtr, uint32 dataSize) {
// Constructors
//////////////////////////////////////////////////////////////////////////
-SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0) {
+SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0), _entity(kEntityPlayer) {
}
SaveLoad::~SaveLoad() {
clear(true);
+ _savegame = NULL;
// Zero passed pointers
_engine = NULL;
@@ -481,10 +482,10 @@ void SaveLoad::clear(bool clearStream) {
// Save & Load
//////////////////////////////////////////////////////////////////////////
-// Load game
-void SaveLoad::loadGame(GameId id) {
+// Load last saved game
+void SaveLoad::loadLastGame() {
if (!_savegame)
- error("[SaveLoad::loadGame] No savegame stream present");
+ error("[SaveLoad::loadLastGame] No savegame stream present");
// Rewind current savegame
_savegame->seek(0);
@@ -521,7 +522,29 @@ void SaveLoad::loadGame(GameId id) {
}
// Load a specific game entry
-void SaveLoad::loadGame(GameId id, uint32 index) {
+void SaveLoad::loadGame(uint32 index) {
+ if (!_savegame)
+ error("[SaveLoad::loadLastGame] No savegame stream present");
+
+ // Rewind current savegame
+ _savegame->seek(0);
+
+ // Write main header (with selected index)
+ SavegameMainHeader header;
+ header.count = index;
+ header.brightness = getState()->brightness;
+ header.volume = getState()->volume;
+
+ Common::Serializer ser(NULL, _savegame);
+ header.saveLoadWithSerializer(ser);
+
+ // TODO
+ // Go to the entry
+ // Load the entry
+ // Get offset (main and entry)
+ // Write main header again with correct entry offset
+ // Setup game and start
+
error("[SaveLoad::loadGame] Not implemented! (only loading the last entry is working for now)");
}
@@ -550,7 +573,7 @@ void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) {
entry.saveLoadWithSerializer(ser);
if (!entry.isValid()) {
- error("[SaveLoad::saveGame] Invalid entry. This savegame might be corrupted");
+ warning("[SaveLoad::saveGame] Invalid entry. This savegame might be corrupted");
_savegame->seek(header.offset);
} else if (getState()->time < entry.time || (type == kSavegameTypeTickInterval && getState()->time == entry.time)) {
// Not ready to save a game, skipping!
@@ -634,6 +657,9 @@ bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *he
// Entries
//////////////////////////////////////////////////////////////////////////
uint32 SaveLoad::writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) {
+ if (!_savegame)
+ error("[SaveLoad::writeValue] Stream not initialized properly");
+
debugC(kLastExpressDebugSavegame, "Savegame: Writing %s: %u bytes", name, size);
uint32 prevPosition = (uint32)_savegame->pos();
@@ -652,6 +678,9 @@ uint32 SaveLoad::writeValue(Common::Serializer &ser, const char *name, Common::F
}
uint32 SaveLoad::readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) {
+ if (!_savegame)
+ error("[SaveLoad::readValue] Stream not initialized properly");
+
debugC(kLastExpressDebugSavegame, "Savegame: Reading %s: %u bytes", name, size);
uint32 prevPosition = (uint32)_savegame->pos();
diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h
index 8656b2ee86..361957227e 100644
--- a/engines/lastexpress/game/savegame.h
+++ b/engines/lastexpress/game/savegame.h
@@ -153,8 +153,8 @@ public:
uint32 init(GameId id, bool resetHeaders);
// Save & Load
- void loadGame(GameId id);
- void loadGame(GameId id, uint32 index);
+ void loadLastGame();
+ void loadGame(uint32 index);
void saveGame(SavegameType type, EntityIndex entity, uint32 value);
void loadVolumeBrightness();
diff --git a/engines/lastexpress/game/savepoint.cpp b/engines/lastexpress/game/savepoint.cpp
index 6b2dfc5930..8d14ec386b 100644
--- a/engines/lastexpress/game/savepoint.cpp
+++ b/engines/lastexpress/game/savepoint.cpp
@@ -202,7 +202,7 @@ void SavePoints::callAndProcess() {
// Misc
//////////////////////////////////////////////////////////////////////////
bool SavePoints::updateEntityFromData(const SavePoint &savepoint) {
- for (int i = 0; i < (int)_data.size(); i++) {
+ for (uint i = 0; i < _data.size(); i++) {
// Not a data savepoint!
if (!_data[i].entity1)
@@ -210,7 +210,7 @@ bool SavePoints::updateEntityFromData(const SavePoint &savepoint) {
// Found our data!
if (_data[i].entity1 == savepoint.entity1 && _data[i].action == savepoint.action) {
- debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%d", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param);
+ debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%u", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param);
// the SavePoint param value is the index of the entity call parameter to update
getEntities()->get(_data[i].entity1)->getParamData()->updateParameters(_data[i].param);
diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp
index 3cda900757..a2c7226b93 100644
--- a/engines/lastexpress/game/scenes.cpp
+++ b/engines/lastexpress/game/scenes.cpp
@@ -739,7 +739,7 @@ void SceneManager::resetQueue() {
_queue.clear();
}
-void SceneManager::setCoordinates(Common::Rect rect) {
+void SceneManager::setCoordinates(const Common::Rect &rect) {
_flagCoordinates = true;
if (_coords.right > rect.right)
diff --git a/engines/lastexpress/game/scenes.h b/engines/lastexpress/game/scenes.h
index a866c65111..1c7ae85f98 100644
--- a/engines/lastexpress/game/scenes.h
+++ b/engines/lastexpress/game/scenes.h
@@ -79,7 +79,7 @@ public:
void removeAndRedraw(SequenceFrame **frame, bool doRedraw);
void resetQueue();
void setCoordinates(SequenceFrame *frame);
- void setCoordinates(Common::Rect rect);
+ void setCoordinates(const Common::Rect &rect);
// Helpers
SceneIndex getSceneIndexFromPosition(CarIndex car, Position position, int param3 = -1);
diff --git a/engines/lastexpress/menu/menu.cpp b/engines/lastexpress/menu/menu.cpp
index 6a453aee99..c48e55bb55 100644
--- a/engines/lastexpress/menu/menu.cpp
+++ b/engines/lastexpress/menu/menu.cpp
@@ -916,13 +916,13 @@ void Menu::startGame() {
if (_lastIndex == _index) {
setGlobalTimer(0);
if (_index) {
- getSaveLoad()->loadGame(_gameId);
+ getSaveLoad()->loadLastGame();
} else {
getLogic()->resetState();
getEntities()->setup(true, kEntityPlayer);
}
} else {
- getSaveLoad()->loadGame(_gameId, _index);
+ getSaveLoad()->loadGame(_index);
}
}
diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp
index f2a063e45f..3d22657124 100644
--- a/engines/lastexpress/sound/entry.cpp
+++ b/engines/lastexpress/sound/entry.cpp
@@ -116,10 +116,8 @@ void SoundEntry::close() {
}
void SoundEntry::play() {
- if (!_stream) {
+ if (!_stream)
error("[SoundEntry::play] stream has been disposed");
- return;
- }
// Prepare sound stream
if (!_soundStream)
diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp
index d72acfd8a0..8904b48930 100644
--- a/engines/lastexpress/sound/queue.cpp
+++ b/engines/lastexpress/sound/queue.cpp
@@ -67,6 +67,8 @@ void SoundQueue::handleTimer() {
for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) {
SoundEntry *entry = (*i);
+ if (entry == NULL)
+ error("[SoundQueue::handleTimer] Invalid entry found in sound queue");
// When the entry has stopped playing, we remove his buffer
if (entry->isFinished()) {
@@ -123,6 +125,8 @@ void SoundQueue::updateQueue() {
for (Common::List<SoundEntry *>::iterator it = _soundList.begin(); it != _soundList.end(); ++it) {
SoundEntry *entry = *it;
+ if (entry == NULL)
+ error("[SoundQueue::updateQueue] Invalid entry found in sound queue");
// Original removes the entry data from the cache and sets the archive as not loaded
// and if the sound data buffer is not full, loads a new entry to be played based on
@@ -179,6 +183,8 @@ void SoundQueue::clearQueue() {
for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) {
SoundEntry *entry = (*i);
+ if (entry == NULL)
+ error("[SoundQueue::clearQueue] Invalid entry found in sound queue");
// Delete entry
entry->close();
diff --git a/engines/lure/decode.cpp b/engines/lure/decode.cpp
index 1338559534..484126c43f 100644
--- a/engines/lure/decode.cpp
+++ b/engines/lure/decode.cpp
@@ -255,7 +255,7 @@ MemoryBlock *PictureDecoder::vgaDecode(MemoryBlock *src, uint32 maxOutputSize) {
decrCtr();
if (shlCarry())
break;
-
+
AL = dataIn->data()[BP + 3];
} else {
decrCtr();
@@ -375,7 +375,7 @@ uint32 AnimationDecoder::decode_data(MemoryBlock *src, MemoryBlock *dest, uint32
// Main loop
bool loopFlag = true;
while (loopFlag) {
- for (;;) {
+ for (;;) {
carry = false;
rcl(currData, carry);
if (--bitCtr == 0) {
diff --git a/engines/made/screenfx.cpp b/engines/made/screenfx.cpp
index de9231a158..ad71f1fb49 100644
--- a/engines/made/screenfx.cpp
+++ b/engines/made/screenfx.cpp
@@ -200,10 +200,10 @@ void ScreenEffects::stepBlendedPalette() {
setBlendedPalette(_blendedPaletteStatus._palette, _blendedPaletteStatus._newPalette,
_blendedPaletteStatus._colorCount, _blendedPaletteStatus._value, _blendedPaletteStatus._maxValue);
if (_blendedPaletteStatus._value == _blendedPaletteStatus._maxValue)
- _blendedPaletteStatus._value++;
- else
- _blendedPaletteStatus._value = MIN<int16>(_blendedPaletteStatus._value + _blendedPaletteStatus._incr, _blendedPaletteStatus._maxValue);
- }
+ _blendedPaletteStatus._value++;
+ else
+ _blendedPaletteStatus._value = MIN<int16>(_blendedPaletteStatus._value + _blendedPaletteStatus._incr, _blendedPaletteStatus._maxValue);
+ }
}
void ScreenEffects::copyFxRect(Graphics::Surface *surface, int16 x1, int16 y1, int16 x2, int16 y2) {
diff --git a/engines/mohawk/bitmap.cpp b/engines/mohawk/bitmap.cpp
index 952b6daec2..bc19fe2d3e 100644
--- a/engines/mohawk/bitmap.cpp
+++ b/engines/mohawk/bitmap.cpp
@@ -630,7 +630,7 @@ void MohawkBitmap::drawRLE8(Graphics::Surface *surface, bool isLE) {
// Myst Bitmap Decoder
//////////////////////////////////////////
-MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream* stream) {
+MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream *stream) {
uint32 uncompressedSize = stream->readUint32LE();
Common::SeekableReadStream *bmpStream = decompressLZ(stream, uncompressedSize);
delete stream;
@@ -652,10 +652,10 @@ MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream* stream) {
}
// Copy the palette to one of our own
- const byte *palette = bitmapDecoder.getPalette();
byte *newPal = 0;
- if (palette) {
+ if (bitmapDecoder.hasPalette()) {
+ const byte *palette = bitmapDecoder.getPalette();
newPal = (byte *)malloc(256 * 3);
memcpy(newPal, palette, 256 * 3);
}
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index 0ed4f38b53..b1b99722d5 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -478,7 +478,7 @@ VideoHandle VideoManager::findVideoHandle(const Common::String &filename) {
return NULL_VID_HANDLE;
}
-int32 VideoManager::getCurFrame(VideoHandle handle) {
+int VideoManager::getCurFrame(VideoHandle handle) {
assert(handle != NULL_VID_HANDLE);
return _videoStreams[handle]->getCurFrame();
}
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index 9dddcde09b..6d2783936d 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -98,7 +98,7 @@ public:
// Handle functions
VideoHandle findVideoHandle(uint16 id);
VideoHandle findVideoHandle(const Common::String &filename);
- int32 getCurFrame(VideoHandle handle);
+ int getCurFrame(VideoHandle handle);
uint32 getFrameCount(VideoHandle handle);
uint32 getTime(VideoHandle handle);
uint32 getDuration(VideoHandle videoHandle);
diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp
new file mode 100644
index 0000000000..134e5cfbf3
--- /dev/null
+++ b/engines/parallaction/adlib.cpp
@@ -0,0 +1,808 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/system.h"
+
+#include "audio/fmopl.h"
+#include "audio/mpu401.h"
+#include "audio/softsynth/emumidi.h"
+
+namespace Parallaction {
+
+const uint kNumVoices = 9;
+// adlib FM voices 0-5
+const uint kNumMelodic = 6;
+// adlib FM voice 6 and 7-8
+const uint kNumPercussion = 5;
+
+// mask for maximum volume level
+#define LEVEL_MASK 0x7f
+
+struct OPLOperator {
+ uint8 characteristic; // amplitude modulation, vibrato, envelope, keyboard scaling, modulator frequency
+ uint8 levels;
+ uint8 attackDecay;
+ uint8 sustainRelease;
+ uint8 waveform;
+};
+
+struct MelodicProgram {
+ OPLOperator op[2];
+ uint8 feedbackAlgo;
+};
+
+struct PercussionNote {
+ OPLOperator op[2];
+ uint8 feedbackAlgo;
+ uint8 percussion;
+ uint8 valid;
+ uint16 frequency;
+ uint8 octave;
+};
+
+static const MelodicProgram melodicPrograms[128] = {
+ {{{ 0x1, 0x51, 0xf2, 0xb2, 0x0 }, { 0x11, 0x0, 0xf2, 0xa2, 0x0 }}, 0x0 },
+ {{{ 0xc2, 0x4b, 0xf1, 0x53, 0x0 }, { 0xd2, 0x0, 0xf2, 0x74, 0x0 }}, 0x4 },
+ {{{ 0x81, 0x9d, 0xf2, 0x74, 0x0 }, { 0x13, 0x0, 0xf2, 0xf1, 0x0 }}, 0x6 },
+ {{{ 0x3, 0x4f, 0xf1, 0x53, 0x0 }, { 0x17, 0x3, 0xf2, 0x74, 0x0 }}, 0x6 },
+ {{{ 0xd1, 0x81, 0x81, 0x73, 0x2 }, { 0xd4, 0x0, 0xe1, 0x34, 0x0 }}, 0x3 },
+ {{{ 0x1, 0x0, 0x94, 0xa6, 0x0 }, { 0x2, 0x0, 0x83, 0x26, 0x0 }}, 0x1 },
+ {{{ 0xf3, 0x84, 0x81, 0x2, 0x1 }, { 0x55, 0x80, 0xdd, 0x3, 0x0 }}, 0x4 },
+ {{{ 0x5, 0x8a, 0xf2, 0x26, 0x0 }, { 0x1, 0x80, 0xf3, 0x48, 0x0 }}, 0x0 },
+ {{{ 0x32, 0x0, 0xb1, 0x14, 0x0 }, { 0x12, 0x0, 0xfd, 0x36, 0x0 }}, 0x3 },
+ {{{ 0x1, 0x0, 0x82, 0xa, 0x2 }, { 0x2, 0x0, 0x85, 0x15, 0x0 }}, 0x3 },
+ {{{ 0xd1, 0x1, 0x97, 0xaa, 0x0 }, { 0x4, 0xd, 0xf3, 0xa5, 0x1 }}, 0x9 },
+ {{{ 0x17, 0x0, 0xf2, 0x62, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 },
+ {{{ 0x6, 0x0, 0xff, 0xf4, 0x0 }, { 0xc4, 0x0, 0xf8, 0xb5, 0x0 }}, 0xe },
+ {{{ 0xc0, 0x81, 0xf2, 0x13, 0x2 }, { 0xc0, 0xc1, 0xf3, 0x14, 0x2 }}, 0xb },
+ {{{ 0x44, 0x53, 0xf5, 0x31, 0x0 }, { 0x60, 0x80, 0xfd, 0x22, 0x0 }}, 0x6 },
+ {{{ 0xe0, 0x80, 0xf4, 0xf2, 0x0 }, { 0x61, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 },
+ {{{ 0xc1, 0x6, 0x83, 0x23, 0x0 }, { 0xc1, 0x4, 0xf0, 0x26, 0x0 }}, 0x1 },
+ {{{ 0x26, 0x0, 0xf4, 0xb6, 0x0 }, { 0x21, 0x0, 0x81, 0x4b, 0x0 }}, 0x1 },
+ {{{ 0x24, 0x80, 0xff, 0xf, 0x0 }, { 0x21, 0x80, 0xff, 0xf, 0x0 }}, 0x1 },
+ {{{ 0x24, 0x4f, 0xf2, 0xb, 0x0 }, { 0x31, 0x0, 0x52, 0xb, 0x0 }}, 0xb },
+ {{{ 0x31, 0x8, 0x81, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0x0 },
+ {{{ 0x70, 0xc5, 0x52, 0x11, 0x1 }, { 0x71, 0x80, 0x31, 0xfe, 0x1 }}, 0x0 },
+ {{{ 0x51, 0x88, 0x10, 0xf0, 0x0 }, { 0x42, 0x83, 0x40, 0xfc, 0x0 }}, 0x8 },
+ {{{ 0xf0, 0xd9, 0x81, 0x3, 0x0 }, { 0xb1, 0x80, 0xf1, 0x5, 0x0 }}, 0xa },
+ {{{ 0x21, 0x4f, 0xf1, 0x31, 0x0 }, { 0x2, 0x80, 0xc3, 0x45, 0x0 }}, 0x0 },
+ {{{ 0x7, 0x8f, 0x9c, 0x33, 0x1 }, { 0x1, 0x80, 0x8a, 0x13, 0x0 }}, 0x0 },
+ {{{ 0x21, 0x40, 0xf1, 0x31, 0x0 }, { 0x6, 0x80, 0xf4, 0x44, 0x0 }}, 0x0 },
+ {{{ 0x21, 0x40, 0xf1, 0x31, 0x3 }, { 0x81, 0x0, 0xf4, 0x44, 0x2 }}, 0x2 },
+ {{{ 0x11, 0x8d, 0xfd, 0x11, 0x0 }, { 0x11, 0x80, 0xfd, 0x11, 0x0 }}, 0x8 },
+ {{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 },
+ {{{ 0xf1, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 },
+ {{{ 0xcd, 0x9e, 0x55, 0xd1, 0x0 }, { 0xd1, 0x0, 0xf2, 0x71, 0x0 }}, 0xe },
+ {{{ 0x1, 0x0, 0xf2, 0x88, 0x0 }, { 0x1, 0x0, 0xf5, 0x88, 0x0 }}, 0x1 },
+ {{{ 0x30, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0x6 },
+ {{{ 0x0, 0x10, 0xf4, 0xd9, 0x0 }, { 0x0, 0x0, 0xf5, 0xd7, 0x0 }}, 0x4 },
+ {{{ 0x1, 0x4c, 0xf2, 0x50, 0x0 }, { 0x1, 0x40, 0xd2, 0x59, 0x0 }}, 0x8 },
+ {{{ 0x20, 0x11, 0xe2, 0x8a, 0x0 }, { 0x20, 0x0, 0xe4, 0xa8, 0x0 }}, 0xa },
+ {{{ 0x21, 0x40, 0x7b, 0x4, 0x1 }, { 0x21, 0x0, 0x75, 0x72, 0x0 }}, 0x2 },
+ {{{ 0x31, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0xa },
+ {{{ 0x1, 0xc, 0xf5, 0x2f, 0x1 }, { 0x0, 0x80, 0xf5, 0x5c, 0x0 }}, 0x0 },
+ {{{ 0xb0, 0x1c, 0x81, 0x3, 0x2 }, { 0x20, 0x0, 0x54, 0x67, 0x2 }}, 0xe },
+ {{{ 0x1, 0x0, 0xf1, 0x65, 0x0 }, { 0x1, 0x80, 0xa3, 0xa8, 0x2 }}, 0x1 },
+ {{{ 0xe1, 0x4f, 0xc1, 0xd3, 0x2 }, { 0x21, 0x0, 0x32, 0x74, 0x1 }}, 0x0 },
+ {{{ 0x2, 0x0, 0xf6, 0x16, 0x0 }, { 0x12, 0x0, 0xf2, 0xf8, 0x0 }}, 0x1 },
+ {{{ 0xe0, 0x63, 0xf8, 0xf3, 0x0 }, { 0x70, 0x80, 0xf7, 0xf3, 0x0 }}, 0x4 },
+ {{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 },
+ {{{ 0x21, 0x16, 0xb0, 0x81, 0x1 }, { 0x22, 0x0, 0xb3, 0x13, 0x1 }}, 0xc },
+ {{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x30, 0x0, 0x90, 0xf, 0x0 }}, 0x6 },
+ {{{ 0x0, 0x10, 0xf1, 0xf2, 0x2 }, { 0x1, 0x0, 0xf1, 0xf2, 0x3 }}, 0x0 },
+ {{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 },
+ {{{ 0xb1, 0x3, 0x55, 0x3, 0x0 }, { 0xb1, 0x3, 0x8, 0xa, 0x0 }}, 0x9 },
+ {{{ 0x22, 0x0, 0xa9, 0x34, 0x1 }, { 0x1, 0x0, 0xa2, 0x42, 0x2 }}, 0x2 },
+ {{{ 0xa0, 0xdc, 0x81, 0x31, 0x3 }, { 0xb1, 0x80, 0xf1, 0x1, 0x3 }}, 0x0 },
+ {{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 },
+ {{{ 0xf1, 0x80, 0xa0, 0x72, 0x0 }, { 0x74, 0x0, 0x90, 0x22, 0x0 }}, 0x9 },
+ {{{ 0xe1, 0x13, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0xf0, 0xfc, 0x1 }}, 0xa },
+ {{{ 0x31, 0x1c, 0x41, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe },
+ {{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe },
+ {{{ 0x21, 0x1c, 0x53, 0x1d, 0x0 }, { 0xa1, 0x80, 0x52, 0x3b, 0x0 }}, 0xc },
+ {{{ 0x21, 0x1d, 0xa4, 0xae, 0x1 }, { 0x21, 0x0, 0xb1, 0x9e, 0x0 }}, 0xc },
+ {{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa },
+ {{{ 0xe1, 0x15, 0x71, 0xae, 0x0 }, { 0xe2, 0x0, 0x81, 0x9e, 0x0 }}, 0xe },
+ {{{ 0x21, 0x16, 0x71, 0xae, 0x0 }, { 0x21, 0x0, 0x81, 0x9e, 0x0 }}, 0xe },
+ {{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe },
+ {{{ 0x21, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa },
+ {{{ 0x22, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa },
+ {{{ 0x23, 0x4f, 0x81, 0x53, 0x0 }, { 0x34, 0x0, 0x22, 0x2c, 0x0 }}, 0xa },
+ {{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa },
+ {{{ 0x71, 0xc5, 0x6e, 0x17, 0x0 }, { 0x22, 0x5, 0x8b, 0xe, 0x0 }}, 0x2 },
+ {{{ 0xe6, 0x27, 0x70, 0xf, 0x1 }, { 0xe3, 0x0, 0x60, 0x9f, 0x0 }}, 0xa },
+ {{{ 0x30, 0xc8, 0xd5, 0x19, 0x0 }, { 0xb1, 0x80, 0x61, 0x1b, 0x0 }}, 0xc },
+ {{{ 0x32, 0x9a, 0x51, 0x1b, 0x0 }, { 0xa1, 0x82, 0xa2, 0x3b, 0x0 }}, 0xc },
+ {{{ 0xad, 0x3, 0x74, 0x29, 0x0 }, { 0xa2, 0x82, 0x73, 0x29, 0x0 }}, 0x7 },
+ {{{ 0x21, 0x83, 0x74, 0x17, 0x0 }, { 0x62, 0x8d, 0x65, 0x17, 0x0 }}, 0x7 },
+ {{{ 0x94, 0xb, 0x85, 0xff, 0x1 }, { 0x13, 0x0, 0x74, 0xff, 0x0 }}, 0xc },
+ {{{ 0x74, 0x87, 0xa4, 0x2, 0x0 }, { 0xd6, 0x80, 0x45, 0x42, 0x0 }}, 0x2 },
+ {{{ 0xb3, 0x85, 0x76, 0x21, 0x1 }, { 0x20, 0x0, 0x3d, 0xc1, 0x0 }}, 0x6 },
+ {{{ 0x17, 0x4f, 0xf2, 0x61, 0x0 }, { 0x12, 0x8, 0xf1, 0xb4, 0x0 }}, 0x8 },
+ {{{ 0x4f, 0x86, 0x65, 0x1, 0x0 }, { 0x1f, 0x0, 0x32, 0x74, 0x0 }}, 0x4 },
+ {{{ 0xe1, 0x23, 0x71, 0xae, 0x0 }, { 0xe4, 0x0, 0x82, 0x9e, 0x0 }}, 0xa },
+ {{{ 0x11, 0x86, 0xf2, 0xbd, 0x0 }, { 0x4, 0x80, 0xa0, 0x9b, 0x1 }}, 0x8 },
+ {{{ 0x20, 0x90, 0xf5, 0x9e, 0x2 }, { 0x11, 0x0, 0xf4, 0x5b, 0x3 }}, 0xc },
+ {{{ 0xf0, 0x80, 0x34, 0xe4, 0x0 }, { 0x7e, 0x0, 0xa2, 0x6, 0x0 }}, 0x8 },
+ {{{ 0x90, 0xf, 0xff, 0x1, 0x3 }, { 0x0, 0x0, 0x1f, 0x1, 0x0 }}, 0xe },
+ {{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x33, 0x0, 0x90, 0xf, 0x0 }}, 0x6 },
+ {{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 },
+ {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 },
+ {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 },
+ {{{ 0x93, 0x6, 0xc1, 0x4, 0x1 }, { 0x82, 0x0, 0x51, 0x9, 0x0 }}, 0x6 },
+ {{{ 0xa0, 0x0, 0x96, 0x33, 0x0 }, { 0x20, 0x0, 0x55, 0x2b, 0x0 }}, 0x6 },
+ {{{ 0x0, 0xc0, 0xff, 0x5, 0x0 }, { 0x0, 0x0, 0xff, 0x5, 0x3 }}, 0x0 },
+ {{{ 0x4, 0x8, 0xf8, 0x7, 0x0 }, { 0x1, 0x0, 0x82, 0x74, 0x0 }}, 0x8 },
+ {{{ 0x0, 0x0, 0x2f, 0x5, 0x0 }, { 0x20, 0x0, 0xff, 0x5, 0x3 }}, 0xa },
+ {{{ 0x93, 0x0, 0xf7, 0x7, 0x2 }, { 0x0, 0x0, 0xf7, 0x7, 0x0 }}, 0xa },
+ {{{ 0x0, 0x40, 0x80, 0x7a, 0x0 }, { 0xc4, 0x0, 0xc0, 0x7e, 0x0 }}, 0x8 },
+ {{{ 0x90, 0x80, 0x55, 0xf5, 0x0 }, { 0x0, 0x0, 0x55, 0xf5, 0x0 }}, 0x8 },
+ {{{ 0xe1, 0x80, 0x34, 0xe4, 0x0 }, { 0x69, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 },
+ {{{ 0x3, 0x2, 0xf0, 0xff, 0x3 }, { 0x11, 0x80, 0xf0, 0xff, 0x2 }}, 0x2 },
+ {{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 },
+ {{{ 0x0, 0x0, 0x2f, 0x1, 0x0 }, { 0x0, 0x0, 0xff, 0x1, 0x0 }}, 0x4 },
+ {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 },
+ {{{ 0x93, 0x85, 0x3f, 0x6, 0x1 }, { 0x0, 0x0, 0x5f, 0x7, 0x0 }}, 0x6 },
+ {{{ 0x6, 0x0, 0xa0, 0xf0, 0x0 }, { 0x44, 0x0, 0xc5, 0x75, 0x0 }}, 0xe },
+ {{{ 0x60, 0x0, 0x10, 0x81, 0x0 }, { 0x20, 0x8c, 0x12, 0x91, 0x0 }}, 0xe },
+ {{{ 0x1, 0x40, 0xf1, 0x53, 0x0 }, { 0x8, 0x40, 0xf1, 0x53, 0x0 }}, 0x0 },
+ {{{ 0x31, 0x0, 0x56, 0x31, 0x0 }, { 0x16, 0x0, 0x7d, 0x41, 0x0 }}, 0x0 },
+ {{{ 0x0, 0x10, 0xf2, 0x72, 0x0 }, { 0x13, 0x0, 0xf2, 0x72, 0x0 }}, 0xc },
+ {{{ 0x10, 0x0, 0x75, 0x93, 0x1 }, { 0x1, 0x0, 0xf5, 0x82, 0x1 }}, 0x0 },
+ {{{ 0x0, 0x0, 0xf6, 0xff, 0x2 }, { 0x0, 0x0, 0xf6, 0xff, 0x0 }}, 0x8 },
+ {{{ 0x30, 0x0, 0xff, 0xa0, 0x3 }, { 0x63, 0x0, 0x65, 0xb, 0x2 }}, 0x0 },
+ {{{ 0x2a, 0x0, 0xf6, 0x87, 0x0 }, { 0x2b, 0x0, 0x76, 0x25, 0x0 }}, 0x0 },
+ {{{ 0x85, 0x0, 0xb8, 0x84, 0x0 }, { 0x43, 0x0, 0xe5, 0x8f, 0x0 }}, 0x6 },
+ {{{ 0x7, 0x4f, 0xf2, 0x60, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 },
+ {{{ 0x5, 0x40, 0xb3, 0xd3, 0x0 }, { 0x86, 0x80, 0xf2, 0x24, 0x0 }}, 0x2 },
+ {{{ 0xd0, 0x0, 0x11, 0xcf, 0x0 }, { 0xd1, 0x0, 0xf4, 0xe8, 0x3 }}, 0x0 },
+ {{{ 0x5, 0x4e, 0xda, 0x25, 0x2 }, { 0x1, 0x0, 0xf9, 0x15, 0x0 }}, 0xa },
+ {{{ 0x3, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 },
+ {{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xf9, 0x5, 0x0 }}, 0x0 },
+ {{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 },
+ {{{ 0xf1, 0x41, 0x11, 0x11, 0x0 }, { 0xf1, 0x41, 0x11, 0x11, 0x0 }}, 0x2 },
+ {{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 },
+ {{{ 0x1, 0x0, 0x2f, 0x1, 0x0 }, { 0x1, 0x0, 0xaf, 0x1, 0x3 }}, 0xf },
+ {{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 },
+ {{{ 0xc0, 0x4f, 0xf1, 0x3, 0x0 }, { 0xbe, 0xc, 0x10, 0x1, 0x0 }}, 0x2 },
+ {{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 },
+ {{{ 0x81, 0x47, 0xf1, 0x83, 0x0 }, { 0xa2, 0x4, 0x91, 0x86, 0x0 }}, 0x6 },
+ {{{ 0xf0, 0xc0, 0xff, 0xff, 0x3 }, { 0xe5, 0x0, 0xfb, 0xf0, 0x0 }}, 0xe },
+ {{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 }
+};
+
+static const PercussionNote percussionNotes[47] = {
+ {{{ 0x0, 0xb, 0xa8, 0x38, 0x0 }, { 0x0, 0x0, 0xd6, 0x49, 0x0 }}, 0x0, 0x4, 0x1, 0x97, 0x4 },
+ {{{ 0xc0, 0xc0, 0xf8, 0x3f, 0x2 }, { 0xc0, 0x0, 0xf6, 0x8e, 0x0 }}, 0x0, 0x4, 0x1, 0xf7, 0x4 },
+ {{{ 0xc0, 0x80, 0xc9, 0xab, 0x0 }, { 0xeb, 0x40, 0xb5, 0xf6, 0x0 }}, 0x1, 0x3, 0x1, 0x6a, 0x6 },
+ {{{ 0xc, 0x0, 0xd8, 0xa6, 0x0 }, { 0x0, 0x0, 0xd6, 0x4f, 0x0 }}, 0x1, 0x3, 0x1, 0x6c, 0x5 },
+ {{{ 0x1, 0x0, 0xe2, 0xd2, 0x0 }, { 0x3, 0x41, 0x8f, 0x48, 0x49 }}, 0xc, 0x4, 0x1, 0x2f, 0x5 },
+ {{{ 0x0, 0x0, 0xc8, 0x58, 0x3 }, { 0x0, 0x0, 0xf6, 0x4f, 0x0 }}, 0x9, 0x3, 0x1, 0x108, 0x4 },
+ {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf2, 0xff, 0xe0, 0x50, 0x52 }}, 0x5d, 0x2, 0x1, 0x9f, 0x5 },
+ {{{ 0xe, 0x9, 0xb9, 0x47, 0x0 }, { 0xeb, 0x40, 0xf5, 0xe6, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 },
+ {{{ 0x0, 0x0, 0xd6, 0x83, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xc7, 0x5 },
+ {{{ 0x1, 0x9, 0x89, 0x67, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x0, 0x1, 0x80, 0x6 },
+ {{{ 0x1, 0x0, 0xd6, 0x96, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xed, 0x5 },
+ {{{ 0x0, 0x9, 0xa9, 0x55, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 },
+ {{{ 0x2, 0x0, 0xc6, 0x96, 0x0 }, { 0xe0, 0x0, 0xe0, 0x40, 0x0 }}, 0x1, 0x2, 0x1, 0x123, 0x5 },
+ {{{ 0x5, 0x0, 0xf6, 0x56, 0x0 }, { 0xf7, 0xff, 0xb3, 0x90, 0x4f }}, 0x1, 0x2, 0x1, 0x15b, 0x5 },
+ {{{ 0x1, 0x0, 0xf7, 0x14, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x1ac, 0x5 },
+ {{{ 0x0, 0x0, 0xf6, 0x56, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x1, 0x2, 0x1, 0x18b, 0x5 },
+ {{{ 0x0, 0x83, 0xfb, 0x5, 0x0 }, { 0xf7, 0x41, 0x39, 0x90, 0x79 }}, 0x1, 0x1, 0x1, 0xc8, 0x5 },
+ {{{ 0x0, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0xf9, 0x5 },
+ {{{ 0x1, 0x0, 0xa0, 0x5, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x27a, 0x6 },
+ {{{ 0x0, 0x5, 0xf3, 0x6, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x108, 0x7 },
+ {{{ 0x1, 0x0, 0xf9, 0x34, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x147, 0x4 },
+ {{{ 0x0, 0x0, 0xf7, 0x16, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x120, 0x6 },
+ {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x42, 0x6 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x6d, 0x5 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
+ {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }
+};
+
+const uint16 melodicFrequencies[36] = {
+ 0x55, 0x5a, 0x60, 0x66, 0x6c, 0x72, 0x79, 0x80, 0x88,
+ 0x90, 0x99, 0xa1, 0xab, 0xb5, 0xc0, 0xcc, 0xd8, 0xe5,
+ 0xf2, 0x101, 0x110, 0x120, 0x132, 0x143, 0x156, 0x16b, 0x181,
+ 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x286
+};
+
+class AdLibDriver;
+
+class AdLibChannel : public MidiChannel_MPU401 {
+public:
+ void reset();
+
+ uint8 _program;
+ uint8 _volume;
+ uint8 _pedal;
+};
+
+struct MelodicVoice {
+ bool _used;
+ uint8 _channel;
+ uint8 _program;
+
+ uint8 _key;
+ uint32 _timestamp;
+ uint16 _frequency;
+ int8 _octave;
+};
+
+class AdLibDriver : public MidiDriver_Emulated {
+public:
+ AdLibDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) {
+ for (uint i = 0; i < 16; ++i)
+ _channels[i].init(this, i);
+ }
+
+ int open();
+ void close();
+ void send(uint32 b);
+ MidiChannel *allocateChannel();
+ MidiChannel *getPercussionChannel() { return &_channels[9]; }
+
+ bool isStereo() const { return false; }
+ int getRate() const { return _mixer->getOutputRate(); }
+
+ void generateSamples(int16 *buf, int len);
+
+protected:
+ OPL::OPL *_opl;
+ AdLibChannel _channels[16];
+ MelodicVoice _voices[kNumMelodic];
+ uint8 _notesPerPercussion[kNumPercussion];
+
+ uint _lastVoice;
+
+ uint8 _percussionMask;
+
+ void noteOff(uint8 channel, uint8 note);
+ void noteOn(uint8 channel, uint8 note, uint8 velocity);
+ void allNotesOff();
+ void setModulationWheel(uint8 channel, uint8 value);
+ void setFootController(uint8 channel, uint8 value);
+ void setVolume(uint8 channel, uint8 value);
+ void setPitchBend(uint8 channel, int16 value);
+
+ void playNote(uint8 voice, uint8 octave, uint16 frequency);
+
+ void programOperatorSimple(uint8 offset, const OPLOperator &op);
+ void programOperator(uint8 offset, const OPLOperator &op);
+ void setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume);
+
+ void setupPercussion(const PercussionNote &note);
+ void playPercussion(uint8 channel, const PercussionNote &note, uint8 velocity);
+
+ void programMelodicVoice(uint8 voice, uint8 program);
+ void playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity);
+ void muteMelodicVoice(uint8 voice);
+
+ void initVoices();
+};
+
+MidiDriver *createAdLibDriver() {
+ return new AdLibDriver(g_system->getMixer());
+}
+
+void AdLibChannel::reset() {
+ _program = 0;
+ _volume = 127;
+ _pedal = 0;
+}
+
+/*
+ bit 7 - Clear: AM depth is 1 dB
+ bit 6 - Clear: Vibrato depth is 7 cent
+ bit 5 - Set: Rhythm enabled (6 melodic voices)
+ bit 4 - Bass drum off
+ bit 3 - Snare drum off
+ bit 2 - Tom tom off
+ bit 1 - Cymbal off
+ bit 0 - Hi Hat off
+*/
+const uint8 kDefaultPercussionMask = 0x20;
+
+int AdLibDriver::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ MidiDriver_Emulated::open();
+
+ _opl = OPL::Config::create();
+ _opl->init(getRate());
+ _opl->writeReg(0x1, 0x20); // set bit 5 (enable all waveforms)
+
+ // Reset the OPL registers.
+ for (uint i = 0; i < kNumVoices; ++i) {
+ _opl->writeReg(0xA0 + i, 0); // frequency
+ _opl->writeReg(0xB0 + i, 0); // key on
+ _opl->writeReg(0xC0 + i, 0); // feedback
+ }
+ _opl->writeReg(0xBD, kDefaultPercussionMask);
+
+ initVoices();
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+ return 0;
+}
+
+void AdLibDriver::close() {
+ if (!_isOpen)
+ return;
+
+ _isOpen = false;
+ _mixer->stopHandle(_mixerSoundHandle);
+
+ delete _opl;
+}
+
+void AdLibDriver::send(uint32 b) {
+ uint channel = b & 0xf;
+ uint cmd = (b >> 4) & 0xf;
+ uint param1 = (b >> 8) & 0xff;
+ uint param2 = (b >> 16) & 0xff;
+
+ switch (cmd) {
+ case 8:
+ noteOff(channel, param1);
+ break;
+ case 9:
+ // TODO: map volume?
+ noteOn(channel, param1, param2);
+ break;
+ case 11:
+ // controller change
+ switch (param1) {
+ case 1:
+ setModulationWheel(channel, param2);
+ break;
+ case 4:
+ setFootController(channel, param2);
+ break;
+ case 7:
+ setVolume(channel, param2);
+ break;
+ case 123:
+ // all notes off
+ allNotesOff();
+ break;
+ }
+ break;
+ case 12:
+ // program change
+ _channels[channel]._program = param1;
+ break;
+ case 14:
+ setPitchBend(channel, (param1 | (param2 << 7)) - 0x2000);
+ break;
+ }
+}
+
+void AdLibDriver::noteOff(uint8 channel, uint8 note) {
+ if (channel == 9) {
+ if (note < 35 || note > 81)
+ return;
+
+ _percussionMask &= ~(1 << percussionNotes[note - 35].percussion);
+ _opl->writeReg(0xBD, _percussionMask);
+ return;
+ }
+
+ for (int i = kNumMelodic - 1; i >= 0; --i) {
+ if (_voices[i]._channel != channel)
+ continue;
+ if (_voices[i]._key != note)
+ continue;
+ muteMelodicVoice(i);
+ _voices[i]._used = false;
+ return;
+ }
+
+ //debug(1, "failed to find voice off for channel %d, note %d", channel, note);
+}
+
+void AdLibDriver::noteOn(uint8 channel, uint8 note, uint8 velocity) {
+ if (channel == 9) {
+ if (note < 35 || note > 81)
+ return;
+
+ const PercussionNote &info = percussionNotes[note - 35];
+ if (!info.valid)
+ return;
+
+ if (note != _notesPerPercussion[info.percussion]) {
+ setupPercussion(info);
+ _notesPerPercussion[info.percussion] = note;
+ }
+
+ playPercussion(channel, info, velocity);
+ return;
+ }
+
+ if (velocity == 0) {
+ noteOff(channel, note);
+ return;
+ }
+
+ // We want to play a note on a melodic (voice) channel.
+
+ // First, look for a voice playing the same note with the same program.
+ for (uint i = 0; i < kNumMelodic; ++i) {
+ if (_voices[i]._channel != channel || _voices[i]._key != note)
+ continue;
+ if (_voices[i]._program != _channels[channel]._program)
+ continue;
+ muteMelodicVoice(i);
+ playMelodicNote(i, channel, note, velocity);
+ return;
+ }
+
+ // The loops below try to start at _lastVoice and find a voice to use.
+ // They ignore _lastVoice itself, and update _lastVoice if they succeed.
+
+ // Then, try finding a melodic voice with the same program.
+ for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
+ if (_voices[i]._used)
+ continue;
+ if (_voices[i]._program != _channels[channel]._program)
+ continue;
+ playMelodicNote(i, channel, note, velocity);
+ _lastVoice = i;
+ return;
+ }
+
+ // Then, try finding a free melodic voice of any kind.
+ for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
+ if (_voices[i]._used)
+ continue;
+ programMelodicVoice(i, _channels[channel]._program);
+ playMelodicNote(i, channel, note, velocity);
+ _lastVoice = i;
+ return;
+ }
+
+ // Then just try finding a melodic voice with the same program,
+ // and steal it.
+ for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
+ if (_voices[i]._program != _channels[channel]._program)
+ continue;
+ muteMelodicVoice(i);
+ playMelodicNote(i, channel, note, velocity);
+ _lastVoice = i;
+ return;
+ }
+
+ // Finally, just take control of the channel used least recently.
+ uint voiceId = 0;
+ uint32 bestTimestamp = 0xffffffff;
+ for (uint i = 0; i < kNumMelodic; ++i)
+ if (bestTimestamp > _voices[i]._timestamp) {
+ voiceId = i;
+ bestTimestamp = _voices[i]._timestamp;
+ }
+
+ //debug(1, "ran out of voices for channel %d, note %d, program %d: reused voice %d", channel, note, _channels[channel]._program, voiceId);
+ programMelodicVoice(voiceId, _channels[channel]._program);
+ playMelodicNote(voiceId, channel, note, velocity);
+ _lastVoice = voiceId;
+}
+
+// TODO: this doesn't match original
+void AdLibDriver::allNotesOff() {
+ for (uint i = 0; i < kNumMelodic; ++i) {
+ muteMelodicVoice(i);
+ _voices[i]._used = false;
+ }
+
+ _percussionMask = kDefaultPercussionMask;
+ _opl->writeReg(0xBD, kDefaultPercussionMask);
+}
+
+void AdLibDriver::setModulationWheel(uint8 channel, uint8 value) {
+ if (value >= 64)
+ _percussionMask |= 0x80;
+ else
+ _percussionMask &= 0x7f;
+
+ _opl->writeReg(0xBD, _percussionMask);
+}
+
+void AdLibDriver::setFootController(uint8 channel, uint8 value) {
+ _channels[channel]._pedal = (value >= 64);
+}
+
+void AdLibDriver::setVolume(uint8 channel, uint8 value) {
+ _channels[channel]._volume = value;
+}
+
+void AdLibDriver::setPitchBend(uint8 channel, int16 value) {
+ for (uint i = 0; i < kNumMelodic; ++i) {
+ if (_voices[i]._channel != channel || !_voices[i]._used)
+ continue;
+
+ // index into frequency table
+ uint f = 12 + (_voices[i]._key % 12);
+
+ int16 bendAmount = value;
+ if (bendAmount > 0) {
+ // bend up two semitones
+ bendAmount *= (melodicFrequencies[f + 2] - melodicFrequencies[f]);
+ } else {
+ // bend down two semitones
+ bendAmount *= (melodicFrequencies[f] - melodicFrequencies[f - 2]);
+ }
+ bendAmount /= 0x2000;
+ bendAmount += melodicFrequencies[f]; // add the base frequency
+ playNote(i, _voices[i]._octave, bendAmount);
+ _voices[i]._timestamp = g_system->getMillis();
+ }
+}
+
+void AdLibDriver::playNote(uint8 voice, uint8 octave, uint16 frequency) {
+ /* Percussions are always fed keyOn = 0 even to set the note, as they are activated using the
+ BD register instead. I wonder if they can just be fed the same value as melodic voice and
+ be done with it. */
+ uint8 keyOn = (voice < kNumMelodic) ? 0x20 : 0;
+
+ // key on, octave, high 2 bits of frequency
+ _opl->writeReg(0xB0 + voice, keyOn | ((octave & 7) << 2) | ((frequency >> 8) & 3));
+ // low 8 bits of frequency
+ _opl->writeReg(0xA0 + voice, frequency & 0xff);
+}
+
+void AdLibDriver::programOperatorSimple(uint8 offset, const OPLOperator &op) {
+ _opl->writeReg(0x40 + offset, op.levels & LEVEL_MASK);
+ _opl->writeReg(0x60 + offset, op.attackDecay);
+ _opl->writeReg(0x80 + offset, op.sustainRelease);
+}
+
+void AdLibDriver::programOperator(uint8 offset, const OPLOperator &op) {
+ _opl->writeReg(0x20 + offset, op.characteristic);
+ _opl->writeReg(0x60 + offset, op.attackDecay);
+ _opl->writeReg(0x80 + offset, op.sustainRelease);
+ _opl->writeReg(0xE0 + offset, op.waveform);
+ _opl->writeReg(0x40 + offset, op.levels);
+}
+
+const uint16 adlibLogVolume[] = {
+ 0, 37, 58, 73, 85, 95, 103, 110, 116, 121, 127, 131, 135, 139, 143, 146,
+ 149, 153, 155, 158, 161, 163, 165, 168, 170, 172, 174, 176, 178, 179, 181, 183,
+ 184, 186, 188, 189, 191, 192, 193, 195, 196, 197, 198, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 219,
+ 220, 221, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 231,
+ 232, 233, 233, 234, 234, 235, 236, 236, 237, 237, 238, 239, 239, 240, 240, 241,
+ 241, 242, 242, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249,
+ 249, 250, 250, 251, 251, 252, 252, 253, 253, 253, 254, 254, 255, 255, 256, 256,
+ 256
+};
+
+void AdLibDriver::setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume) {
+ uint8 programLevel = LEVEL_MASK;
+ if (!forceVolume)
+ programLevel -= (op.levels & LEVEL_MASK);
+
+ uint32 noteLevel = adlibLogVolume[velocity];
+ uint32 channelLevel = adlibLogVolume[_channels[channel]._volume];
+ // programLevel comes from the static data and is probably already in the correct logarithmic scale
+ uint32 finalLevel = LEVEL_MASK - ((noteLevel * channelLevel * programLevel) >> 16);
+
+ // high 2 bits are scaling level, the rest is (inversed) volume
+ _opl->writeReg(0x40 + offset, (op.levels & 0xc0) | (finalLevel & 0x3f));
+}
+
+const uint8 operatorOffsetsForPercussion[] = {
+ 0x11, // hi-hat
+ 0x15, // cymbal
+ 0x12, // tom tom
+ 0x14 // snare drum
+};
+
+void AdLibDriver::setupPercussion(const PercussionNote &note) {
+ if (note.percussion < 4) {
+ // simple percussion (1 operator)
+
+ // turn off relevant percussion
+ _percussionMask &= ~(1 << note.percussion);
+ _opl->writeReg(0xBD, _percussionMask);
+
+ programOperatorSimple(operatorOffsetsForPercussion[note.percussion], note.op[0]);
+ return;
+ }
+
+ // bass drum (2 operators)
+
+ // turn off bass drum
+ _percussionMask &= ~(0x10);
+ _opl->writeReg(0xBD, _percussionMask);
+
+ programOperator(0x10, note.op[0]);
+ programOperator(0x13, note.op[1]);
+
+ _opl->writeReg(0xC0 + 6, note.feedbackAlgo);
+}
+
+void AdLibDriver::playPercussion(uint8 channel, const PercussionNote &note, uint8 velocity) {
+ if (note.percussion < 4) {
+ // simple percussion (1 operator)
+
+ // turn off relevant percussion
+ _percussionMask &= ~(1 << note.percussion);
+ _opl->writeReg(0xBD, _percussionMask);
+
+ setOperatorLevel(operatorOffsetsForPercussion[note.percussion], note.op[0], velocity, channel, true);
+
+ if (note.percussion == 2) {
+ // tom tom
+ playNote(8, note.octave, note.frequency);
+ } else if (note.percussion == 3) {
+ // snare drum
+ playNote(7, note.octave, note.frequency);
+ }
+
+ // turn on relevant percussion
+ _percussionMask |= (1 << note.percussion);
+ _opl->writeReg(0xBD, _percussionMask);
+ return;
+ }
+
+ // turn off bass drum
+ _percussionMask &= ~(0x10);
+ _opl->writeReg(0xBD, _percussionMask);
+
+ if (note.feedbackAlgo & 1) {
+ // operators 1 and 2 in additive synthesis
+ setOperatorLevel(0x10, note.op[0], velocity, channel, true);
+ setOperatorLevel(0x13, note.op[1], velocity, channel, true);
+ } else {
+ // operator 2 is modulating operator 1
+ setOperatorLevel(0x13, note.op[1], velocity, channel, true);
+ }
+
+ playNote(6, note.octave, note.frequency);
+
+ // turn on bass drum
+ _percussionMask |= 0x10;
+ _opl->writeReg(0xBD, _percussionMask);
+}
+
+const uint8 offset1ForMelodic[kNumVoices] = { 0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12 };
+const uint8 offset2ForMelodic[kNumVoices] = { 0x3, 0x4, 0x5, 0xb, 0xc, 0xd, 0x13, 0x14, 0x15 };
+
+void AdLibDriver::programMelodicVoice(uint8 voice, uint8 program) {
+ assert(program < 128);
+ assert(voice < kNumMelodic);
+
+ const MelodicProgram &info = melodicPrograms[program];
+ uint8 offset1 = offset1ForMelodic[voice];
+ uint8 offset2 = offset2ForMelodic[voice];
+
+ // Start at lowest volume.
+ _opl->writeReg(0x40 + offset1, LEVEL_MASK);
+ _opl->writeReg(0x40 + offset2, LEVEL_MASK);
+
+ muteMelodicVoice(voice);
+
+ programOperator(offset1, info.op[0]);
+ programOperator(offset2, info.op[1]);
+
+ _opl->writeReg(0xC0 + voice, info.feedbackAlgo);
+}
+
+void AdLibDriver::playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity) {
+ assert(voice < kNumMelodic);
+
+ uint8 octave = note / 12;
+ uint8 f = 12 + (note % 12);
+
+ if (octave > 7)
+ octave = 7;
+
+ const MelodicProgram &info = melodicPrograms[_channels[channel]._program];
+ uint8 offset1 = offset1ForMelodic[voice];
+ uint8 offset2 = offset2ForMelodic[voice];
+
+ if (info.feedbackAlgo & 1) {
+ setOperatorLevel(offset1, info.op[0], velocity, channel, false);
+ setOperatorLevel(offset2, info.op[1], velocity, channel, false);
+ } else {
+ setOperatorLevel(offset2, info.op[1], velocity, channel, true);
+ }
+
+ playNote(voice, octave, melodicFrequencies[f]);
+
+ _voices[voice]._program = _channels[channel]._program;
+ _voices[voice]._key = note;
+ _voices[voice]._channel = channel;
+ _voices[voice]._timestamp = g_system->getMillis();
+ _voices[voice]._frequency = melodicFrequencies[f];
+ _voices[voice]._octave = octave;
+ _voices[voice]._used = true;
+}
+
+void AdLibDriver::muteMelodicVoice(uint8 voice) {
+ _opl->writeReg(0xB0 + voice, 0 | ((_voices[voice]._octave & 7) << 2) | ((_voices[voice]._frequency >> 8) & 3));
+}
+
+MidiChannel *AdLibDriver::allocateChannel() {
+ for (uint i = 0; i < 16; ++i) {
+ if (i == 9)
+ continue;
+
+ if (_channels[i].allocate())
+ return &_channels[i];
+ }
+
+ return NULL;
+}
+
+void AdLibDriver::generateSamples(int16 *buf, int len) {
+ memset(buf, 0, sizeof(int16) * len);
+ _opl->readBuffer(buf, len);
+}
+
+void AdLibDriver::initVoices() {
+ _percussionMask = kDefaultPercussionMask;
+ _opl->writeReg(0xBD, _percussionMask);
+
+ for (uint i = 0; i < 16; ++i)
+ _channels[i].reset();
+
+ for (uint i = 0; i < kNumMelodic; ++i) {
+ _voices[i]._key = 0xff;
+ _voices[i]._program = 0xff;
+ _voices[i]._channel = 0xff;
+ _voices[i]._timestamp = 0;
+ _voices[i]._frequency = 0;
+ _voices[i]._octave = 0;
+ _voices[i]._used = false;
+ }
+
+ for (uint i = 0; i < kNumPercussion; ++i)
+ _notesPerPercussion[i] = 0xff;
+
+ _lastVoice = 0;
+}
+
+} // namespace Parallaction
diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp
index 64885c7ff3..c1720a1a8e 100644
--- a/engines/parallaction/callables_ns.cpp
+++ b/engines/parallaction/callables_ns.cpp
@@ -275,7 +275,7 @@ void Parallaction_ns::_c_contaFoglie(void *parm) {
if (num_foglie != 6)
return;
- _globalFlags |= 0x1000;
+ g_globalFlags |= 0x1000;
return;
}
@@ -286,7 +286,7 @@ void Parallaction_ns::_c_zeroFoglie(void *parm) {
}
void Parallaction_ns::_c_trasformata(void *parm) {
- _engineFlags ^= kEngineTransformedDonna;
+ g_engineFlags ^= kEngineTransformedDonna;
// No need to invoke changeCharacter here, as
// transformation happens on a location switch
// and character change is automatically triggered.
@@ -295,11 +295,11 @@ void Parallaction_ns::_c_trasformata(void *parm) {
void Parallaction_ns::_c_offMouse(void *parm) {
_input->setMouseState(MOUSE_DISABLED);
- _engineFlags |= kEngineBlockInput;
+ g_engineFlags |= kEngineBlockInput;
}
void Parallaction_ns::_c_onMouse(void *parm) {
- _engineFlags &= ~kEngineBlockInput;
+ g_engineFlags &= ~kEngineBlockInput;
_input->setMouseState(MOUSE_ENABLED_SHOW);
}
@@ -389,7 +389,7 @@ void Parallaction_ns::_c_finito(void *parm) {
}
void Parallaction_ns::_c_ridux(void *parm) {
- changeCharacter(_minidinoName);
+ changeCharacter(g_minidinoName);
return;
}
@@ -444,7 +444,7 @@ void Parallaction_ns::_c_startIntro(void *parm) {
_soundManI->playMusic();
}
- _engineFlags |= kEngineBlockInput;
+ g_engineFlags |= kEngineBlockInput;
_input->setMouseState(MOUSE_DISABLED);
_intro = true;
}
diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp
index 0cb329e0f0..25acac9b06 100644
--- a/engines/parallaction/debug.cpp
+++ b/engines/parallaction/debug.cpp
@@ -103,7 +103,7 @@ bool Debugger::Cmd_Locations(int argc, const char **argv) {
bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) {
- uint32 flags = _globalFlags;
+ uint32 flags = g_globalFlags;
DebugPrintf("+------------------------------+---------+\n"
"| flag name | value |\n"
@@ -128,10 +128,10 @@ bool Debugger::Cmd_ToggleGlobalFlag(int argc, const char **argv) {
DebugPrintf("invalid flag '%s'\n", argv[1]);
} else {
i--;
- if ((_globalFlags & (1 << i)) == 0)
- _globalFlags |= (1 << i);
+ if ((g_globalFlags & (1 << i)) == 0)
+ g_globalFlags |= (1 << i);
else
- _globalFlags &= ~(1 << i);
+ g_globalFlags &= ~(1 << i);
}
break;
diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp
index e0bd6a6677..78cc23311f 100644
--- a/engines/parallaction/dialogue.cpp
+++ b/engines/parallaction/dialogue.cpp
@@ -192,7 +192,7 @@ void DialogueManager::transitionToState(DialogueState newState) {
bool DialogueManager::testAnswerFlags(Answer *a) {
uint32 flags = _vm->getLocationFlags();
if (a->_yesFlags & kFlagsGlobal)
- flags = _globalFlags | kFlagsGlobal;
+ flags = g_globalFlags | kFlagsGlobal;
return ((a->_yesFlags & flags) == a->_yesFlags) && ((a->_noFlags & ~flags) == a->_noFlags);
}
@@ -370,9 +370,9 @@ protected:
bool _askPassword;
bool checkPassword() {
- return ((!scumm_stricmp(_vm->_char.getBaseName(), _doughName) && _vm->_password.hasPrefix("1732461")) ||
- (!scumm_stricmp(_vm->_char.getBaseName(), _donnaName) && _vm->_password.hasPrefix("1622")) ||
- (!scumm_stricmp(_vm->_char.getBaseName(), _dinoName) && _vm->_password.hasPrefix("179")));
+ return ((!scumm_stricmp(_vm->_char.getBaseName(), g_doughName) && _vm->_password.hasPrefix("1732461")) ||
+ (!scumm_stricmp(_vm->_char.getBaseName(), g_donnaName) && _vm->_password.hasPrefix("1622")) ||
+ (!scumm_stricmp(_vm->_char.getBaseName(), g_dinoName) && _vm->_password.hasPrefix("179")));
}
void resetPassword() {
diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp
index ee981a2c7d..8988897456 100644
--- a/engines/parallaction/disk_br.cpp
+++ b/engines/parallaction/disk_br.cpp
@@ -31,7 +31,7 @@
namespace Parallaction {
-extern byte _braAmigaFramesDefaultPalette[];
+extern byte braAmigaFramesDefaultPalette[];
struct Sprite {
uint16 size;
@@ -475,7 +475,7 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) {
}
delete stream;
} else {
- p = _braAmigaFramesDefaultPalette;
+ p = braAmigaFramesDefaultPalette;
for (i = 0; i < 16; i++) {
r = *p >> 2;
p++;
diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp
index 8d4afd6847..718265664f 100644
--- a/engines/parallaction/disk_ns.cpp
+++ b/engines/parallaction/disk_ns.cpp
@@ -328,7 +328,7 @@ GfxObj* DosDisk_ns::loadTalk(const char *name) {
}
char v20[30];
- if (_engineFlags & kEngineTransformedDonna) {
+ if (g_engineFlags & kEngineTransformedDonna) {
sprintf(v20, "%stta.cnv", name);
} else {
sprintf(v20, "%stal.cnv", name);
diff --git a/engines/parallaction/exec.cpp b/engines/parallaction/exec.cpp
index 8594d02641..122abf9e0e 100644
--- a/engines/parallaction/exec.cpp
+++ b/engines/parallaction/exec.cpp
@@ -56,7 +56,7 @@ void ProgramExec::runScript(ProgramPtr script, AnimationPtr a) {
}
void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator last) {
- if (_engineFlags & kEnginePauseJobs) {
+ if (g_engineFlags & kEnginePauseJobs) {
return;
}
@@ -110,7 +110,7 @@ void CommandExec::runList(CommandList::iterator first, CommandList::iterator las
}
if (cmd->_flagsOn & kFlagsGlobal) {
- useFlags = _globalFlags | kFlagsGlobal;
+ useFlags = g_globalFlags | kFlagsGlobal;
useLocalFlags = false;
} else {
useFlags = _vm->getLocationFlags();
@@ -182,7 +182,7 @@ void CommandExec::suspend() {
}
void CommandExec::runSuspended() {
- if (_engineFlags & kEngineWalking) {
+ if (g_engineFlags & kEngineWalking) {
return;
}
diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp
index 658ef5af82..985ea29311 100644
--- a/engines/parallaction/exec_br.cpp
+++ b/engines/parallaction/exec_br.cpp
@@ -307,7 +307,7 @@ DECLARE_COMMAND_OPCODE(testsfx) {
DECLARE_COMMAND_OPCODE(ret) {
- _engineFlags |= kEngineReturn;
+ g_engineFlags |= kEngineReturn;
}
@@ -327,7 +327,7 @@ DECLARE_INSTRUCTION_OPCODE(invalid) {
DECLARE_COMMAND_OPCODE(clear) {
if (ctxt._cmd->_flags & kFlagsGlobal) {
ctxt._cmd->_flags &= ~kFlagsGlobal;
- _globalFlags &= ~ctxt._cmd->_flags;
+ g_globalFlags &= ~ctxt._cmd->_flags;
} else {
_vm->clearLocationFlags(ctxt._cmd->_flags);
}
@@ -356,7 +356,7 @@ DECLARE_COMMAND_OPCODE(get) {
DECLARE_COMMAND_OPCODE(toggle) {
if (ctxt._cmd->_flags & kFlagsGlobal) {
ctxt._cmd->_flags &= ~kFlagsGlobal;
- _globalFlags ^= ctxt._cmd->_flags;
+ g_globalFlags ^= ctxt._cmd->_flags;
} else {
_vm->toggleLocationFlags(ctxt._cmd->_flags);
}
@@ -373,7 +373,7 @@ DECLARE_COMMAND_OPCODE(invalid) {
DECLARE_COMMAND_OPCODE(set) {
if (ctxt._cmd->_flags & kFlagsGlobal) {
ctxt._cmd->_flags &= ~kFlagsGlobal;
- _globalFlags |= ctxt._cmd->_flags;
+ g_globalFlags |= ctxt._cmd->_flags;
} else {
_vm->setLocationFlags(ctxt._cmd->_flags);
}
diff --git a/engines/parallaction/exec_ns.cpp b/engines/parallaction/exec_ns.cpp
index d8fbdea971..3ea4332e50 100644
--- a/engines/parallaction/exec_ns.cpp
+++ b/engines/parallaction/exec_ns.cpp
@@ -151,7 +151,7 @@ DECLARE_INSTRUCTION_OPCODE(call) {
DECLARE_INSTRUCTION_OPCODE(wait) {
- if (_engineFlags & kEngineWalking) {
+ if (g_engineFlags & kEngineWalking) {
ctxt._ip--;
ctxt._suspend = true;
}
@@ -198,7 +198,7 @@ DECLARE_COMMAND_OPCODE(invalid) {
DECLARE_COMMAND_OPCODE(set) {
if (ctxt._cmd->_flags & kFlagsGlobal) {
ctxt._cmd->_flags &= ~kFlagsGlobal;
- _globalFlags |= ctxt._cmd->_flags;
+ g_globalFlags |= ctxt._cmd->_flags;
} else {
_vm->setLocationFlags(ctxt._cmd->_flags);
}
@@ -208,7 +208,7 @@ DECLARE_COMMAND_OPCODE(set) {
DECLARE_COMMAND_OPCODE(clear) {
if (ctxt._cmd->_flags & kFlagsGlobal) {
ctxt._cmd->_flags &= ~kFlagsGlobal;
- _globalFlags &= ~ctxt._cmd->_flags;
+ g_globalFlags &= ~ctxt._cmd->_flags;
} else {
_vm->clearLocationFlags(ctxt._cmd->_flags);
}
@@ -267,7 +267,7 @@ DECLARE_COMMAND_OPCODE(call) {
DECLARE_COMMAND_OPCODE(toggle) {
if (ctxt._cmd->_flags & kFlagsGlobal) {
ctxt._cmd->_flags &= ~kFlagsGlobal;
- _globalFlags ^= ctxt._cmd->_flags;
+ g_globalFlags ^= ctxt._cmd->_flags;
} else {
_vm->toggleLocationFlags(ctxt._cmd->_flags);
}
diff --git a/engines/parallaction/font.cpp b/engines/parallaction/font.cpp
index d4c9aefd32..3b40960381 100644
--- a/engines/parallaction/font.cpp
+++ b/engines/parallaction/font.cpp
@@ -29,7 +29,7 @@
namespace Parallaction {
-extern byte _amigaTopazFont[];
+extern byte amigaTopazFont[];
class BraFont : public Font {
@@ -675,7 +675,7 @@ void Parallaction_ns::initFonts() {
_introFont = _disk->loadFont("slide");
} else {
_dialogueFont = _disk->loadFont("comic");
- Common::MemoryReadStream stream(_amigaTopazFont, 2600, DisposeAfterUse::NO);
+ Common::MemoryReadStream stream(amigaTopazFont, 2600, DisposeAfterUse::NO);
_labelFont = new AmigaFont(stream);
_menuFont = _disk->loadFont("slide");
_introFont = _disk->loadFont("intro");
diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp
index 852235ce34..8ef3c0fabf 100644
--- a/engines/parallaction/gfxbase.cpp
+++ b/engines/parallaction/gfxbase.cpp
@@ -152,22 +152,22 @@ void Gfx::freeCharacterObjects() {
freeDialogueObjects();
}
-void BackgroundInfo::loadGfxObjMask(const char *name, GfxObj *obj) {
+void BackgroundInfo::loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj) {
debugC(1, kDebugGraphics, "BackgroundInfo::loadGfxObjMask(\"%s\")", name);
Common::Rect rect;
obj->getRect(0, rect);
- MaskBuffer *buf = _vm->_disk->loadMask(name, rect.width(), rect.height());
+ MaskBuffer *buf = vm->_disk->loadMask(name, rect.width(), rect.height());
obj->_maskId = addMaskPatch(buf);
obj->_hasMask = true;
}
-void BackgroundInfo::loadGfxObjPath(const char *name, GfxObj *obj) {
+void BackgroundInfo::loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj) {
Common::Rect rect;
obj->getRect(0, rect);
- PathBuffer *buf = _vm->_disk->loadPath(name, rect.width(), rect.height());
+ PathBuffer *buf = vm->_disk->loadPath(name, rect.width(), rect.height());
obj->_pathId = addPathPatch(buf);
obj->_hasPath = true;
diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp
index 9855830478..59cd02e6ef 100644
--- a/engines/parallaction/graphics.cpp
+++ b/engines/parallaction/graphics.cpp
@@ -80,11 +80,11 @@ void drawCircle(int xCenter, int yCenter, int radius, int color, void (*plotProc
Palette::Palette() {
- int gameType = _vm->getGameType();
+ int gameType = g_vm->getGameType();
if (gameType == GType_Nippon) {
_colors = 32;
- _hb = (_vm->getPlatform() == Common::kPlatformAmiga);
+ _hb = (g_vm->getPlatform() == Common::kPlatformAmiga);
} else
if (gameType == GType_BRA) {
_colors = 256;
diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h
index f8cb4b3647..e9daabb194 100644
--- a/engines/parallaction/graphics.h
+++ b/engines/parallaction/graphics.h
@@ -377,14 +377,14 @@ public:
void toggleMaskPatch(uint id, int x, int y, bool apply);
uint16 getMaskLayer(uint16 z) const;
void finalizeMask();
- void loadGfxObjMask(const char *name, GfxObj *obj);
+ void loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj);
// path management
bool hasPath();
uint addPathPatch(PathBuffer *patch);
void togglePathPatch(uint id, int x, int y, bool apply);
void finalizePath();
- void loadGfxObjPath(const char *name, GfxObj *obj);
+ void loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj);
};
diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp
index 3794aeae29..082c37f666 100644
--- a/engines/parallaction/gui_ns.cpp
+++ b/engines/parallaction/gui_ns.cpp
@@ -787,7 +787,7 @@ public:
}
destroyLabels();
- _engineFlags &= ~kEngineBlockInput;
+ g_engineFlags &= ~kEngineBlockInput;
return _helper->getState("selectcharacter");
}
diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp
index 453bf9849d..4fbd9b99cc 100644
--- a/engines/parallaction/input.cpp
+++ b/engines/parallaction/input.cpp
@@ -188,15 +188,15 @@ int Input::updateGameInput() {
int event = kEvNone;
if (!isMouseEnabled() ||
- (_engineFlags & kEngineBlockInput) ||
- (_engineFlags & kEngineWalking) ||
- (_engineFlags & kEngineChangeLocation)) {
+ (g_engineFlags & kEngineBlockInput) ||
+ (g_engineFlags & kEngineWalking) ||
+ (g_engineFlags & kEngineChangeLocation)) {
debugC(3, kDebugInput, "updateGameInput: input flags (mouse: %i, block: %i, walking: %i, changeloc: %i)",
isMouseEnabled(),
- (_engineFlags & kEngineBlockInput) == 0,
- (_engineFlags & kEngineWalking) == 0,
- (_engineFlags & kEngineChangeLocation) == 0
+ (g_engineFlags & kEngineBlockInput) == 0,
+ (g_engineFlags & kEngineWalking) == 0,
+ (g_engineFlags & kEngineChangeLocation) == 0
);
return event;
@@ -289,7 +289,7 @@ void Input::walkTo(const Common::Point &dest) {
bool Input::translateGameInput() {
- if (_engineFlags & kEnginePauseJobs) {
+ if (g_engineFlags & kEnginePauseJobs) {
return false;
}
@@ -312,7 +312,7 @@ bool Input::translateGameInput() {
// test if mouse is hovering on an interactive zone for the currently selected inventory item
ZonePtr z = _vm->hitZone(_activeItem._id, mousePos.x, mousePos.y);
- if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) {
+ if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((g_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) {
walkTo(mousePos);
return true;
}
@@ -361,7 +361,7 @@ void Input::enterInventoryMode() {
if (hitCharacter) {
if (_activeItem._id != 0) {
_activeItem._index = (_activeItem._id >> 16) & 0xFFFF;
- _engineFlags |= kEngineDragging;
+ g_engineFlags |= kEngineDragging;
} else {
setArrowCursor();
}
@@ -384,9 +384,9 @@ void Input::exitInventoryMode() {
int pos = _vm->getHoverInventoryItem(mousePos.x, mousePos.y);
_vm->highlightInventoryItem(-1); // disable
- if ((_engineFlags & kEngineDragging)) {
+ if ((g_engineFlags & kEngineDragging)) {
- _engineFlags &= ~kEngineDragging;
+ g_engineFlags &= ~kEngineDragging;
ZonePtr z = _vm->hitZone(kZoneMerge, _activeItem._index, _vm->getInventoryItemIndex(pos));
if (z) {
diff --git a/engines/parallaction/module.mk b/engines/parallaction/module.mk
index d65653cd92..36572a51df 100644
--- a/engines/parallaction/module.mk
+++ b/engines/parallaction/module.mk
@@ -1,6 +1,7 @@
MODULE := engines/parallaction
MODULE_OBJS := \
+ adlib.o \
balloons.o \
callables_br.o \
callables_ns.o \
diff --git a/engines/parallaction/objects.cpp b/engines/parallaction/objects.cpp
index d3529c5dd9..50556c3ec4 100644
--- a/engines/parallaction/objects.cpp
+++ b/engines/parallaction/objects.cpp
@@ -203,7 +203,7 @@ Zone::Zone() {
}
Zone::~Zone() {
- _vm->_gfx->unregisterLabel(_label);
+ g_vm->_gfx->unregisterLabel(_label);
delete _label;
}
@@ -325,7 +325,7 @@ int16 ScriptVar::getValue() {
}
if (_flags & kParaRandom) {
- return (_vm->_rnd.getRandomNumber(65536) * _value) >> 16;
+ return (g_vm->_rnd.getRandomNumber(65536) * _value) >> 16;
}
error("Parameter is not an r-value");
diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp
index 3b1b7d54a0..e6ef53aa78 100644
--- a/engines/parallaction/parallaction.cpp
+++ b/engines/parallaction/parallaction.cpp
@@ -33,12 +33,12 @@
#include "parallaction/walk.h"
namespace Parallaction {
-Parallaction *_vm = NULL;
+Parallaction *g_vm = NULL;
// public stuff
-char _saveData1[30] = { '\0' };
-uint32 _engineFlags = 0;
-uint32 _globalFlags = 0;
+char g_saveData1[30] = { '\0' };
+uint32 g_engineFlags = 0;
+uint32 g_globalFlags = 0;
// private stuff
@@ -48,7 +48,7 @@ Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gam
// Setup mixer
syncSoundSettings();
- _vm = this;
+ g_vm = this;
DebugMan.addDebugChannel(kDebugDialogue, "dialogue", "Dialogues debug level");
DebugMan.addDebugChannel(kDebugParser, "parser", "Parser debug level");
DebugMan.addDebugChannel(kDebugDisk, "disk", "Disk debug level");
@@ -87,7 +87,7 @@ Parallaction::~Parallaction() {
Common::Error Parallaction::init() {
_gameType = getGameType();
- _engineFlags = 0;
+ g_engineFlags = 0;
_objectsNames = NULL;
_globalFlagsNames = NULL;
_location._hasSound = false;
@@ -129,13 +129,9 @@ GUI::Debugger *Parallaction::getDebugger() {
return _debugger;
}
-bool canScroll() {
- return (_vm->_gfx->_backgroundInfo->width > _vm->_screenWidth);
-}
-
void Parallaction::updateView() {
- if ((_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) {
+ if ((g_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) {
return;
}
@@ -147,14 +143,14 @@ void Parallaction::updateView() {
void Parallaction::pauseJobs() {
debugC(9, kDebugExec, "pausing jobs execution");
- _engineFlags |= kEnginePauseJobs;
+ g_engineFlags |= kEnginePauseJobs;
return;
}
void Parallaction::resumeJobs() {
debugC(9, kDebugExec, "resuming jobs execution");
- _engineFlags &= ~kEnginePauseJobs;
+ g_engineFlags &= ~kEnginePauseJobs;
return;
}
@@ -265,7 +261,7 @@ void Parallaction::runGameFrame(int event) {
if (shouldQuit())
return;
- if (_engineFlags & kEngineChangeLocation) {
+ if (g_engineFlags & kEngineChangeLocation) {
changeLocation();
}
@@ -900,14 +896,14 @@ void CharacterName::bind(const char *name) {
if (!_dummy) {
if (!strcmp(name, "donna")) {
- _engineFlags &= ~kEngineTransformedDonna;
+ g_engineFlags &= ~kEngineTransformedDonna;
} else {
- if (_engineFlags & kEngineTransformedDonna) {
+ if (g_engineFlags & kEngineTransformedDonna) {
_suffix = _suffixTras;
} else {
const char *s = strstr(name, "tras");
if (s) {
- _engineFlags |= kEngineTransformedDonna;
+ g_engineFlags |= kEngineTransformedDonna;
_suffix = _suffixTras;
end = s;
}
@@ -953,7 +949,7 @@ void Parallaction::beep() {
void Parallaction::scheduleLocationSwitch(const char *location) {
debugC(9, kDebugExec, "scheduleLocationSwitch(%s)\n", location);
_newLocationName = location;
- _engineFlags |= kEngineChangeLocation;
+ g_engineFlags |= kEngineChangeLocation;
}
} // End of namespace Parallaction
diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h
index 0d56b62e2f..2dbb0227d6 100644
--- a/engines/parallaction/parallaction.h
+++ b/engines/parallaction/parallaction.h
@@ -104,17 +104,17 @@ struct PARALLACTIONGameDescription;
-extern uint32 _engineFlags;
-extern char _saveData1[];
-extern uint32 _globalFlags;
-extern const char *_dinoName;
-extern const char *_donnaName;
-extern const char *_doughName;
-extern const char *_drkiName;
-extern const char *_minidinoName;
-extern const char *_minidonnaName;
-extern const char *_minidoughName;
-extern const char *_minidrkiName;
+extern uint32 g_engineFlags;
+extern char g_saveData1[];
+extern uint32 g_globalFlags;
+extern const char *g_dinoName;
+extern const char *g_donnaName;
+extern const char *g_doughName;
+extern const char *g_drkiName;
+extern const char *g_minidinoName;
+extern const char *g_minidonnaName;
+extern const char *g_minidoughName;
+extern const char *g_minidrkiName;
@@ -601,7 +601,7 @@ private:
void _c_password(void *);
};
-extern Parallaction *_vm;
+extern Parallaction *g_vm;
} // End of namespace Parallaction
diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp
index 658a8e8795..07755fac5f 100644
--- a/engines/parallaction/parallaction_br.cpp
+++ b/engines/parallaction/parallaction_br.cpp
@@ -79,7 +79,7 @@ Common::Error Parallaction_br::init() {
_cmdExec = new CommandExec_br(this);
_programExec = new ProgramExec_br(this);
- _walker = new PathWalker_BR;
+ _walker = new PathWalker_BR(this);
_part = -1;
_nextPart = -1;
@@ -161,10 +161,10 @@ Common::Error Parallaction_br::go() {
// initCharacter();
- while (((_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) {
+ while (((g_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) {
runGame();
}
- _engineFlags &= ~kEngineReturn;
+ g_engineFlags &= ~kEngineReturn;
cleanupGame();
}
@@ -259,7 +259,7 @@ void Parallaction_br::cleanupGame() {
_countersNames = 0;
_numLocations = 0;
- _globalFlags = 0;
+ g_globalFlags = 0;
memset(_localFlags, 0, sizeof(_localFlags));
memset(_locationNames, 0, sizeof(_locationNames));
memset(_zoneFlags, 0, sizeof(_zoneFlags));
@@ -275,7 +275,7 @@ void Parallaction_br::changeLocation() {
cleanupGame();
// more cleanup needed for part changes (see also saveload)
- _globalFlags = 0;
+ g_globalFlags = 0;
cleanInventory(true);
strcpy(_characterName1, "null");
@@ -358,7 +358,7 @@ void Parallaction_br::changeLocation() {
// TODO: implement the music commands which control music execution
_soundMan->execute(SC_PLAYMUSIC);
- _engineFlags &= ~kEngineChangeLocation;
+ g_engineFlags &= ~kEngineChangeLocation;
_newLocationName.clear();
_nextPart = -1;
}
@@ -548,7 +548,7 @@ void Parallaction_br::scheduleWalk(int16 x, int16 y, bool fromUser) {
}
}
- _engineFlags |= kEngineWalking;
+ g_engineFlags |= kEngineWalking;
}
void Parallaction_br::setFollower(const Common::String &name) {
diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp
index 0b92db1f0a..2a22e77410 100644
--- a/engines/parallaction/parallaction_ns.cpp
+++ b/engines/parallaction/parallaction_ns.cpp
@@ -182,7 +182,7 @@ Common::Error Parallaction_ns::init() {
_cmdExec = new CommandExec_ns(this);
_programExec = new ProgramExec_ns(this);
- _walker = new PathWalker_NS;
+ _walker = new PathWalker_NS(this);
_sarcophagusDeltaX = 0;
_movingSarcophagus = false;
@@ -382,8 +382,8 @@ void Parallaction_ns::changeLocation() {
changeCharacter(locname.character());
}
- strcpy(_saveData1, locname.location());
- parseLocation(_saveData1);
+ strcpy(g_saveData1, locname.location());
+ parseLocation(g_saveData1);
if (_location._startPosition.x != -1000) {
_char._ani->setX(_location._startPosition.x);
@@ -399,7 +399,7 @@ void Parallaction_ns::changeLocation() {
// BUG #1837503: kEngineChangeLocation flag must be cleared *before* commands
// and acommands are executed, so that it can be set again if needed.
- _engineFlags &= ~kEngineChangeLocation;
+ g_engineFlags &= ~kEngineChangeLocation;
_cmdExec->run(_location._commands);
@@ -526,10 +526,10 @@ void Parallaction_ns::cleanupGame() {
_soundManI->stopMusic();
_inTestResult = false;
- _engineFlags &= ~kEngineTransformedDonna;
+ g_engineFlags &= ~kEngineTransformedDonna;
_numLocations = 0;
- _globalFlags = 0;
+ g_globalFlags = 0;
memset(_localFlags, 0, sizeof(_localFlags));
memset(_locationNames, 0, sizeof(_locationNames));
@@ -553,7 +553,7 @@ void Parallaction_ns::scheduleWalk(int16 x, int16 y, bool fromUser) {
}
_walker->buildPath(a, x, y);
- _engineFlags |= kEngineWalking;
+ g_engineFlags |= kEngineWalking;
}
}// namespace Parallaction
diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp
index 0904dbf655..e7f1b1b1ed 100644
--- a/engines/parallaction/parser_br.cpp
+++ b/engines/parallaction/parser_br.cpp
@@ -767,10 +767,10 @@ void LocationParser_br::parseGetData(ZonePtr z) {
data->_gfxobj = obj;
} else
if (!scumm_stricmp(_tokens[0], "mask")) {
- _out->_info->loadGfxObjMask(_tokens[1], data->_gfxobj);
+ _out->_info->loadGfxObjMask(_vm, _tokens[1], data->_gfxobj);
} else
if (!scumm_stricmp(_tokens[0], "path")) {
- _out->_info->loadGfxObjPath(_tokens[1], data->_gfxobj);
+ _out->_info->loadGfxObjPath(_vm, _tokens[1], data->_gfxobj);
} else
if (!scumm_stricmp(_tokens[0], "icon")) {
data->_getIcon = 4 + _vm->_objectsNames->lookup(_tokens[1]);
diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp
index f1d1db53e9..41ff74f0b4 100644
--- a/engines/parallaction/parser_ns.cpp
+++ b/engines/parallaction/parser_ns.cpp
@@ -246,7 +246,7 @@ DECLARE_ANIM_PARSER(file) {
char vC8[200];
strcpy(vC8, _tokens[1]);
- if (_engineFlags & kEngineTransformedDonna) {
+ if (g_engineFlags & kEngineTransformedDonna) {
if (!scumm_stricmp(_tokens[1], "donnap") || !scumm_stricmp(_tokens[1], "donnapa")) {
strcat(vC8, "tras");
}
diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp
index 8de2d89b18..2ecc5377a4 100644
--- a/engines/parallaction/saveload.cpp
+++ b/engines/parallaction/saveload.cpp
@@ -88,7 +88,7 @@ void SaveLoad_ns::doLoadGame(uint16 slot) {
_vm->_score = atoi(s.c_str());
s = f->readLine();
- _globalFlags = atoi(s.c_str());
+ g_globalFlags = atoi(s.c_str());
s = f->readLine();
_vm->_numLocations = atoi(s.c_str());
@@ -151,7 +151,7 @@ void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) {
sprintf(s, "%s\n", _vm->_char.getFullName());
f->writeString(s);
- sprintf(s, "%s\n", _saveData1);
+ sprintf(s, "%s\n", g_saveData1);
f->writeString(s);
sprintf(s, "%d\n", _vm->_char._ani->getX());
f->writeString(s);
@@ -159,7 +159,7 @@ void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) {
f->writeString(s);
sprintf(s, "%d\n", _vm->_score);
f->writeString(s);
- sprintf(s, "%u\n", _globalFlags);
+ sprintf(s, "%u\n", g_globalFlags);
f->writeString(s);
sprintf(s, "%d\n", _vm->_numLocations);
diff --git a/engines/parallaction/sound.h b/engines/parallaction/sound.h
index e875e69334..e12e50e278 100644
--- a/engines/parallaction/sound.h
+++ b/engines/parallaction/sound.h
@@ -33,6 +33,7 @@
#define PATH_LEN 200
class MidiParser;
+class MidiDriver;
namespace Parallaction {
@@ -41,6 +42,7 @@ class MidiPlayer;
class Parallaction_br;
class MidiPlayer_MSC;
+MidiDriver *createAdLibDriver();
class SoundManImpl {
public:
diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp
index 0925e55309..ad510eb1f1 100644
--- a/engines/parallaction/sound_br.cpp
+++ b/engines/parallaction/sound_br.cpp
@@ -91,20 +91,20 @@ public:
};
void MidiParser_MSC::parseMetaEvent(EventInfo &info) {
- uint8 type = read1(_position._play_pos);
- uint8 len = read1(_position._play_pos);
+ uint8 type = read1(_position._playPos);
+ uint8 len = read1(_position._playPos);
info.ext.type = type;
info.length = len;
info.ext.data = 0;
if (type == 0x51) {
- info.ext.data = _position._play_pos;
+ info.ext.data = _position._playPos;
} else {
warning("unknown meta event 0x%02X", type);
info.ext.type = 0;
}
- _position._play_pos += len;
+ _position._playPos += len;
}
void MidiParser_MSC::parseMidiEvent(EventInfo &info) {
@@ -116,13 +116,13 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) {
case 0xA:
case 0xB:
case 0xE:
- info.basic.param1 = read1(_position._play_pos);
- info.basic.param2 = read1(_position._play_pos);
+ info.basic.param1 = read1(_position._playPos);
+ info.basic.param2 = read1(_position._playPos);
break;
case 0xC:
case 0xD:
- info.basic.param1 = read1(_position._play_pos);
+ info.basic.param1 = read1(_position._playPos);
info.basic.param2 = 0;
break;
@@ -135,9 +135,9 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) {
}
void MidiParser_MSC::parseNextEvent(EventInfo &info) {
- info.start = _position._play_pos;
+ info.start = _position._playPos;
- if (_position._play_pos >= _trackEnd) {
+ if (_position._playPos >= _trackEnd) {
// fake an end-of-track meta event
info.delta = 0;
info.event = 0xFF;
@@ -146,8 +146,9 @@ void MidiParser_MSC::parseNextEvent(EventInfo &info) {
return;
}
- info.delta = readVLQ(_position._play_pos);
- info.event = read1(_position._play_pos);
+ info.length = 0;
+ info.delta = readVLQ(_position._playPos);
+ info.event = read1(_position._playPos);
if (info.event == 0xFF) {
parseMetaEvent(info);
@@ -155,7 +156,7 @@ void MidiParser_MSC::parseNextEvent(EventInfo &info) {
}
if (info.event < 0x80) {
- _position._play_pos--;
+ _position._playPos--;
info.event = _lastEvent;
}
@@ -185,7 +186,7 @@ bool MidiParser_MSC::loadMusic(byte *data, uint32 size) {
_lastEvent = 0;
_trackEnd = data + size;
- _num_tracks = 1;
+ _numTracks = 1;
_tracks[0] = pos;
setTempo(500000);
@@ -224,7 +225,12 @@ MidiPlayer_MSC::MidiPlayer_MSC()
: _paused(false) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
- _driver = MidiDriver::createMidi(dev);
+ const MusicType musicType = MidiDriver::getMusicType(dev);
+ if (musicType == MT_ADLIB) {
+ _driver = createAdLibDriver();
+ } else {
+ _driver = MidiDriver::createMidi(dev);
+ }
assert(_driver);
int ret = _driver->open();
diff --git a/engines/parallaction/sound_ns.cpp b/engines/parallaction/sound_ns.cpp
index dcc71e4f2f..0ee3d73556 100644
--- a/engines/parallaction/sound_ns.cpp
+++ b/engines/parallaction/sound_ns.cpp
@@ -168,13 +168,13 @@ void DosSoundMan_ns::playCharacterMusic(const char *character) {
char *name = const_cast<char *>(character);
const char *newMusicFile = 0;
- if (!scumm_stricmp(name, _dinoName)) {
+ if (!scumm_stricmp(name, g_dinoName)) {
newMusicFile = "dino";
} else
- if (!scumm_stricmp(name, _donnaName)) {
+ if (!scumm_stricmp(name, g_donnaName)) {
newMusicFile = "donna";
} else
- if (!scumm_stricmp(name, _doughName)) {
+ if (!scumm_stricmp(name, g_doughName)) {
newMusicFile = "nuts";
} else {
warning("unknown character '%s' in DosSoundMan_ns_ns::playCharacterMusic", character);
diff --git a/engines/parallaction/staticres.cpp b/engines/parallaction/staticres.cpp
index 73e78cae3c..f09b1241bc 100644
--- a/engines/parallaction/staticres.cpp
+++ b/engines/parallaction/staticres.cpp
@@ -89,14 +89,14 @@ byte Input::_resMouseArrow_BR_Amiga[512] = {
/*
This palette snippet is used for animations in Big Red Adventure.
*/
-byte _braAmigaFramesDefaultPalette[48] = {
+byte braAmigaFramesDefaultPalette[48] = {
0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00,
0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE,
0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF,
};
-byte _amigaTopazFont[2600] = {
+byte amigaTopazFont[2600] = {
0x00, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x02, 0x79,
0x70, 0xff, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
@@ -263,7 +263,7 @@ byte _amigaTopazFont[2600] = {
};
-const char *_callableNamesRes_ns[] = {
+const char *callableNamesRes_ns[] = {
"Projector",
"HBOff",
"StartIntro",
@@ -292,7 +292,7 @@ const char *_callableNamesRes_ns[] = {
};
-const char *_callableNamesRes_br[] = {
+const char *callableNamesRes_br[] = {
"blufade",
"resetpalette",
"ferrcycle",
@@ -301,15 +301,15 @@ const char *_callableNamesRes_br[] = {
"password"
};
-const char *_dinoName = "dino";
-const char *_donnaName = "donna";
-const char *_doughName = "dough";
-const char *_drkiName = "drki";
+const char *g_dinoName = "dino";
+const char *g_donnaName = "donna";
+const char *g_doughName = "dough";
+const char *g_drkiName = "drki";
-const char *_minidinoName = "minidino";
-const char *_minidonnaName = "minidonna";
-const char *_minidoughName = "minidough";
-const char *_minidrkiName = "minidrki";
+const char *g_minidinoName = "minidino";
+const char *g_minidonnaName = "minidonna";
+const char *g_minidoughName = "minidough";
+const char *g_minidrkiName = "minidrki";
#define CALLABLE_NS(x) &Parallaction_ns::x
@@ -391,7 +391,7 @@ const Parallaction_br::Callable Parallaction_br::_amigaCallables[] = {
void Parallaction_ns::initResources() {
- _callableNames = new Table(ARRAYSIZE(_callableNamesRes_ns), _callableNamesRes_ns);
+ _callableNames = new Table(ARRAYSIZE(callableNamesRes_ns), callableNamesRes_ns);
_localFlagNames = new FixedTable(NUM_LOCATIONS, 1);
_localFlagNames->addData("visited");
@@ -406,7 +406,7 @@ void Parallaction_ns::initResources() {
void Parallaction_br::initResources() {
- _callableNames = new Table(ARRAYSIZE(_callableNamesRes_br), _callableNamesRes_br);
+ _callableNames = new Table(ARRAYSIZE(callableNamesRes_br), callableNamesRes_br);
_localFlagNames = new FixedTable(NUM_LOCATIONS, 2);
_localFlagNames->addData("visited");
diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp
index 53237db4ef..19162cd7db 100644
--- a/engines/parallaction/walk.cpp
+++ b/engines/parallaction/walk.cpp
@@ -55,27 +55,27 @@ WalkFrames _char24WalkFrames_NS = {
};
static int getPathWidth() {
- if (!_vm->_gfx->_backgroundInfo->_path) {
+ if (!g_vm->_gfx->_backgroundInfo->_path) {
warning("getPathWidth() _path is NULL!");
return 0;
} else
- return _vm->_gfx->_backgroundInfo->_path->w;
+ return g_vm->_gfx->_backgroundInfo->_path->w;
}
static int getPathHeight() {
- if (!_vm->_gfx->_backgroundInfo->_path) {
+ if (!g_vm->_gfx->_backgroundInfo->_path) {
warning("getPathHeight() _path is NULL!");
return 0;
} else
- return _vm->_gfx->_backgroundInfo->_path->h;
+ return g_vm->_gfx->_backgroundInfo->_path->h;
}
static bool isPathClear(uint16 x, uint16 y) {
- if (!_vm->_gfx->_backgroundInfo->_path) {
+ if (!g_vm->_gfx->_backgroundInfo->_path) {
warning("isPathClear() _path is NULL!");
return false;
} else
- return (_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false);
+ return (g_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false);
}
// adjusts position towards nearest walkable point
@@ -306,7 +306,7 @@ void PathWalker_NS::checkDoor(const Common::Point &foot) {
}
void PathWalker_NS::finalizeWalk() {
- _engineFlags &= ~kEngineWalking;
+ g_engineFlags &= ~kEngineWalking;
Common::Point foot;
_a->getFoot(foot);
@@ -316,7 +316,7 @@ void PathWalker_NS::finalizeWalk() {
}
void PathWalker_NS::walk() {
- if ((_engineFlags & kEngineWalking) == 0) {
+ if ((g_engineFlags & kEngineWalking) == 0) {
return;
}
@@ -382,7 +382,7 @@ void PathWalker_NS::updateDirection(const Common::Point& pos, const Common::Poin
_a->setF(frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction]);
}
-PathWalker_NS::PathWalker_NS() : _direction(WALK_DOWN), _step(0) {
+PathWalker_NS::PathWalker_NS(Parallaction *vm) : _direction(WALK_DOWN), _step(0), _vm(vm) {
}
bool PathWalker_BR::directPathExists(const Common::Point &from, const Common::Point &to) {
@@ -481,7 +481,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
}
void PathWalker_BR::finalizeWalk(State &s) {
- _engineFlags &= ~kEngineWalking;
+ g_engineFlags &= ~kEngineWalking;
Common::Point foot;
_character._a->getFoot(foot);
@@ -508,8 +508,8 @@ void PathWalker_BR::finalizeWalk(State &s) {
#if 0
// TODO: Input::walkTo must be extended to support destination frame in addition to coordinates
- if (_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput()
- _engineFlags &= ~FINAL_WALK_FRAME;
+ if (g_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput()
+ g_engineFlags &= ~FINAL_WALK_FRAME;
_ch._a->_frame = _moveToF; // from readInput()...
} else {
_ch._a->_frame = _dirFrame; // from walk()
@@ -523,7 +523,7 @@ void PathWalker_BR::finalizeWalk(State &s) {
}
void PathWalker_BR::walk() {
- if ((_engineFlags & kEngineWalking) == 0) {
+ if ((g_engineFlags & kEngineWalking) == 0) {
return;
}
@@ -714,7 +714,7 @@ void PathWalker_BR::doWalk(State &s) {
}
}
-PathWalker_BR::PathWalker_BR() {
+PathWalker_BR::PathWalker_BR(Parallaction *vm) : _vm(vm) {
_character._active = false;
_character._step = 0;
_follower._active = false;
diff --git a/engines/parallaction/walk.h b/engines/parallaction/walk.h
index 6796991f9d..ae6db6eaf1 100644
--- a/engines/parallaction/walk.h
+++ b/engines/parallaction/walk.h
@@ -49,8 +49,10 @@ class PathWalker_NS {
void checkDoor(const Common::Point &foot);
void updateDirection(const Common::Point& pos, const Common::Point& to);
+ Parallaction *_vm;
+
public:
- PathWalker_NS();
+ PathWalker_NS(Parallaction *vm);
void buildPath(AnimationPtr a, uint16 x, uint16 y);
void walk();
@@ -79,8 +81,10 @@ class PathWalker_BR {
void doWalk(State &s);
void checkTrap(const Common::Point &p);
+ Parallaction *_vm;
+
public:
- PathWalker_BR();
+ PathWalker_BR(Parallaction *vm);
~PathWalker_BR() { }
void setCharacterPath(AnimationPtr a, uint16 x, uint16 y);
diff --git a/engines/pegasus/ai/ai_action.cpp b/engines/pegasus/ai/ai_action.cpp
new file mode 100644
index 0000000000..38d639038f
--- /dev/null
+++ b/engines/pegasus/ai/ai_action.cpp
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/ai/ai_action.h"
+#include "pegasus/ai/ai_area.h"
+
+namespace Pegasus {
+
+AICompoundAction::~AICompoundAction() {
+ for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++)
+ delete *it;
+}
+
+void AICompoundAction::performAIAction(AIRule *rule) {
+ for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++)
+ (*it)->performAIAction(rule);
+}
+
+AIPlayMessageAction::AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const InputBits interruptionFilter) {
+ _movieName = movieName;
+ _keepLastFrame = keepLastFrame;
+ _interruptionFilter = interruptionFilter;
+}
+
+void AIPlayMessageAction::performAIAction(AIRule *) {
+ if (g_AIArea) {
+ g_AIArea->checkMiddleArea();
+ g_AIArea->playAIMovie(kRightAreaSignature, _movieName, _keepLastFrame, _interruptionFilter);
+ }
+}
+
+AIStartTimerAction::AIStartTimerAction(AITimerCondition *timerCondition) {
+ _timerCondition = timerCondition;
+}
+
+void AIStartTimerAction::performAIAction(AIRule *) {
+ _timerCondition->startTimer();
+}
+
+AIActivateRuleAction::AIActivateRuleAction(AIRule *rule) {
+ _rule = rule;
+}
+
+void AIActivateRuleAction::performAIAction(AIRule *) {
+ _rule->activateRule();
+}
+
+AIDeactivateRuleAction::AIDeactivateRuleAction(AIRule *rule) {
+ _rule = rule;
+}
+
+void AIDeactivateRuleAction::performAIAction(AIRule *) {
+ _rule->deactivateRule();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/ai/ai_action.h b/engines/pegasus/ai/ai_action.h
new file mode 100644
index 0000000000..6eac976f9c
--- /dev/null
+++ b/engines/pegasus/ai/ai_action.h
@@ -0,0 +1,136 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_AI_AIACTION_H
+#define PEGASUS_AI_AIACTION_H
+
+#include "common/list.h"
+
+#include "pegasus/input.h"
+#include "pegasus/types.h"
+
+namespace Pegasus {
+
+class AIRule;
+class AITimerCondition;
+
+/////////////////////////////////////////////
+//
+// AIAction
+
+class AIAction {
+friend class AIRule;
+public:
+ AIAction() { _actionCount = 1; }
+ virtual ~AIAction() {}
+
+ virtual void performAIAction(AIRule *) = 0;
+
+ void setActionCount(const uint32 count) { _actionCount = count; }
+
+protected:
+ uint32 _actionCount;
+};
+
+typedef Common::List<AIAction *> AIActionList;
+
+/////////////////////////////////////////////
+//
+// AICompoundAction
+
+class AICompoundAction : public AIAction {
+public:
+ AICompoundAction() {}
+ virtual ~AICompoundAction();
+
+ void addAction(AIAction *action) { _compoundActions.push_back(action); }
+
+ virtual void performAIAction(AIRule *);
+
+protected:
+ AIActionList _compoundActions;
+};
+
+/////////////////////////////////////////////
+//
+// AIPlayMessageAction
+
+class AIPlayMessageAction : public AIAction {
+public:
+ AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const InputBits = kWarningInterruption);
+
+ virtual void performAIAction(AIRule *);
+
+protected:
+ Common::String _movieName;
+ InputBits _interruptionFilter;
+ bool _keepLastFrame;
+};
+
+/////////////////////////////////////////////
+//
+// AIStartTimerAction
+
+class AIStartTimerAction : public AIAction {
+public:
+ AIStartTimerAction(AITimerCondition *);
+
+ virtual void performAIAction(AIRule *);
+
+protected:
+ AITimerCondition *_timerCondition;
+};
+
+/////////////////////////////////////////////
+//
+// AIActivateRuleAction
+
+class AIActivateRuleAction : public AIAction {
+public:
+ AIActivateRuleAction(AIRule *);
+
+ virtual void performAIAction(AIRule *);
+
+protected:
+ AIRule *_rule;
+};
+
+/////////////////////////////////////////////
+//
+// AIDeactivateRuleAction
+
+class AIDeactivateRuleAction : public AIAction {
+public:
+ AIDeactivateRuleAction(AIRule *rule);
+
+ virtual void performAIAction(AIRule *);
+
+protected:
+ AIRule *_rule;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/ai/ai_area.cpp b/engines/pegasus/ai/ai_area.cpp
new file mode 100644
index 0000000000..5ac8af8812
--- /dev/null
+++ b/engines/pegasus/ai/ai_area.cpp
@@ -0,0 +1,613 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/memstream.h"
+
+#include "pegasus/cursor.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/aichip.h"
+#include "pegasus/items/biochips/biochipitem.h"
+#include "pegasus/items/biochips/opticalchip.h"
+#include "pegasus/items/biochips/pegasuschip.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+AIArea *g_AIArea = 0;
+
+AIArea::AIArea(InputHandler *nextHandler) : InputHandler(nextHandler), _leftAreaMovie(kAILeftAreaID),
+ _middleAreaMovie(kAIMiddleAreaID), _rightAreaMovie(kAIRightAreaID), _AIMovie(kAIMovieID) {
+ g_AIArea = this;
+ _leftAreaOwner = kNoClientSignature;
+ _middleAreaOwner = kNoClientSignature;
+ _rightAreaOwner = kNoClientSignature;
+ _leftInventoryTime = 0xffffffff;
+ _middleInventoryTime = 0xffffffff;
+ _middleBiochipTime = 0xffffffff;
+ _rightBiochipTime = 0xffffffff;
+ _lockCount = 0;
+ startIdling();
+}
+
+AIArea::~AIArea() {
+ if (_middleAreaOwner == kBiochipSignature) {
+ BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+ if (currentBiochip && currentBiochip->isSelected())
+ currentBiochip->giveUpSharedArea();
+ } else if (_middleAreaOwner == kInventorySignature) {
+ InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem();
+ if (currentItem && currentItem->isSelected())
+ currentItem->giveUpSharedArea();
+ }
+
+ stopIdling();
+
+ for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++)
+ delete *it;
+
+ g_AIArea = 0;
+}
+
+// Save last state of AI rules...
+void AIArea::saveAIState() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ delete vm->_aiSaveStream;
+
+ Common::MemoryWriteStreamDynamic out;
+ writeAIRules(&out);
+
+ vm->_aiSaveStream = new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES);
+}
+
+void AIArea::restoreAIState() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ if (vm->_aiSaveStream)
+ readAIRules(vm->_aiSaveStream);
+}
+
+void AIArea::writeAIRules(Common::WriteStream *stream) {
+ _AIRules.writeAIRules(stream);
+}
+
+void AIArea::readAIRules(Common::ReadStream *stream) {
+ _AIRules.readAIRules(stream);
+}
+
+void AIArea::initAIArea() {
+ allocateSurface(Common::Rect(0, 0, 384, 96));
+
+ _leftAreaMovie.shareSurface(this);
+ _leftAreaMovie.initFromMovieFile("Images/Items/Left Area Movie");
+ _leftAreaMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop);
+ _leftAreaMovie.setDisplayOrder(kAILeftAreaOrder);
+ _leftAreaMovie.startDisplaying();
+ _leftAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+
+ _middleAreaMovie.shareSurface(this);
+ _middleAreaMovie.initFromMovieFile("Images/Items/Middle Area Movie");
+ _middleAreaMovie.moveElementTo(kAIMiddleAreaLeft, kAIMiddleAreaTop);
+ _middleAreaMovie.moveMovieBoxTo(kAIMiddleAreaLeft - kAILeftAreaLeft, 0);
+ _middleAreaMovie.setDisplayOrder(kAIMiddleAreaOrder);
+ _middleAreaMovie.startDisplaying();
+ _middleAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+
+ _rightAreaMovie.shareSurface(this);
+ _rightAreaMovie.initFromMovieFile("Images/Items/Right Area Movie");
+ _rightAreaMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop);
+ _rightAreaMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0);
+ _rightAreaMovie.setDisplayOrder(kAIRightAreaOrder);
+ _rightAreaMovie.startDisplaying();
+ _rightAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+
+ _AIMovie.setDisplayOrder(kAIMovieOrder);
+}
+
+void AIArea::setAIVolume(const uint16 volume) {
+ _leftAreaMovie.setVolume(volume);
+ _middleAreaMovie.setVolume(volume);
+ _rightAreaMovie.setVolume(volume);
+}
+
+// There are only so many legal combinations of client/area.
+// Here is the list of supported pairs:
+// kInventorySignature kLeftAreaSignature
+// kInventorySignature kMiddleAreaSignature
+// kBiochipSignature kMiddleAreaSignature
+// kBiochipSignature kRightAreaSignature
+// kAISignature kLeftAreaSignature
+// Further, the kAISignature never sets a static frame time in the left area,
+// but only plays a sequence.
+
+// If this function is called while a sequence is playing, it will just "remember"
+// the time value, so that when the sequence finishes, the new time is asserted.
+
+void AIArea::setAIAreaToTime(const LowerClientSignature client, const LowerAreaSignature area, const TimeValue time) {
+ switch (area) {
+ case kLeftAreaSignature:
+ // Only support kInventorySignature client, since AI never calls SetAIAreaToTime.
+ _leftAreaMovie.setSegment(0, _leftAreaMovie.getDuration());
+
+ if (time == 0xffffffff) {
+ _leftAreaMovie.hide();
+ _leftAreaOwner = kNoClientSignature;
+ } else {
+ setLeftMovieTime(time);
+ }
+ break;
+ case kMiddleAreaSignature:
+ // Only support kInventorySignature and kBiochipSignature clients.
+ _middleAreaMovie.stop();
+ _middleAreaMovie.setFlags(0);
+ _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration());
+
+ if (time == 0xffffffff) {
+ if (client == kInventorySignature) {
+ if (_middleBiochipTime != 0xffffffff) {
+ setMiddleMovieTime(kBiochipSignature, _middleBiochipTime);
+ } else {
+ _middleAreaMovie.hide();
+ _middleAreaOwner = kNoClientSignature;
+ }
+ } else { // client == kBiochipSignature
+ if (_middleInventoryTime != 0xffffffff) {
+ setMiddleMovieTime(kInventorySignature, _middleInventoryTime);
+ } else {
+ _middleAreaMovie.hide();
+ _middleAreaOwner = kNoClientSignature;
+ }
+ }
+ } else {
+ setMiddleMovieTime(client, time);
+ }
+ break;
+ case kRightAreaSignature:
+ // Only support kBiochipSignature client.
+ _rightAreaMovie.setSegment(0, _rightAreaMovie.getDuration());
+
+ if (time == 0xffffffff) {
+ _rightAreaMovie.hide();
+ _rightAreaOwner = kNoClientSignature;
+ } else {
+ setRightMovieTime(time);
+ }
+ break;
+ }
+}
+
+// Plays a sequence on an area. When the sequence ends, the previous image
+// is restored.
+// Also, is input disabled or not?
+// Easy answer: yes.
+
+// There are only so many legal combinations of client/area.
+// Here is the list of supported pairs:
+// kBiochipSignature kMiddleAreaSignature
+// kBiochipSignature kRightAreaSignature
+// kInventorySignature kMiddleAreaSignature
+
+void AIArea::playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ lockAIOut();
+
+ switch (area) {
+ case kLeftAreaSignature:
+ break;
+ case kMiddleAreaSignature:
+ if (_middleAreaOwner == kInventorySignature)
+ _middleInventoryTime = _middleAreaMovie.getTime();
+ else if (_middleAreaOwner == kBiochipSignature)
+ _middleBiochipTime = _middleAreaMovie.getTime();
+
+ _middleAreaMovie.stop();
+ _middleAreaMovie.setFlags(0);
+ _middleAreaMovie.setSegment(start, stop);
+ _middleAreaMovie.setTime(start);
+ _middleAreaMovie.show();
+ _middleAreaMovie.start();
+ vm->_cursor->hide();
+
+ while (_middleAreaMovie.isRunning()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ _middleAreaMovie.stop();
+ vm->_cursor->hideUntilMoved();
+
+ if (_middleAreaOwner == kInventorySignature)
+ setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleInventoryTime);
+ else if (_middleAreaOwner == kBiochipSignature)
+ setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleBiochipTime);
+ else
+ setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, 0xffffffff);
+ break;
+ case kRightAreaSignature:
+ _rightBiochipTime = _rightAreaMovie.getTime();
+ _rightAreaMovie.setSegment(start, stop);
+ _rightAreaMovie.setTime(start);
+ _rightAreaMovie.show();
+ _rightAreaMovie.start();
+ vm->_cursor->hide();
+
+ while (_rightAreaMovie.isRunning()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ _rightAreaMovie.stop();
+ vm->_cursor->hideUntilMoved();
+ setAIAreaToTime(_rightAreaOwner, kRightAreaSignature, _rightBiochipTime);
+ break;
+ }
+
+ unlockAI();
+}
+
+bool AIArea::playAIMovie(const LowerAreaSignature area, const Common::String &movieName, bool keepLastFrame, const InputBits interruptFilter) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ lockAIOut();
+
+ InputDevice.waitInput(interruptFilter);
+ if (_AIMovie.isMovieValid())
+ _AIMovie.releaseMovie();
+
+ _AIMovie.shareSurface(this);
+ _AIMovie.initFromMovieFile(movieName);
+
+ if (area == kLeftAreaSignature) {
+ _AIMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop);
+ _leftAreaMovie.hide();
+ } else {
+ _AIMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop);
+ _AIMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0);
+ _rightAreaMovie.hide();
+ }
+
+ _AIMovie.setTime(0);
+ _AIMovie.startDisplaying();
+ _AIMovie.show();
+ _AIMovie.redrawMovieWorld();
+ _AIMovie.setVolume(vm->getSoundFXLevel());
+ _AIMovie.start();
+ vm->_cursor->hide();
+
+ bool result = true;
+ bool saveAllowed = vm->swapSaveAllowed(false);
+ bool openAllowed = vm->swapLoadAllowed(false);
+
+ while (_AIMovie.isRunning()) {
+ Input input;
+ InputDevice.getInput(input, interruptFilter);
+
+ if (input.anyInput() || vm->shouldQuit() || vm->saveRequested() || vm->loadRequested()) {
+ result = false;
+ break;
+ }
+
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ _AIMovie.stop();
+
+ vm->swapSaveAllowed(saveAllowed);
+ vm->swapLoadAllowed(openAllowed);
+
+ // This used to keep the last frame up even if the movie was interrupted.
+ // However, this only occurs in the recalibration, where interruption means skip the
+ // whole thing, so skipping causes the AI to go away even when keepLastFrame is true.
+
+ if (!(result && keepLastFrame)) {
+ _AIMovie.stopDisplaying();
+ _AIMovie.releaseMovie();
+
+ if (area == kLeftAreaSignature) {
+ _leftAreaMovie.setTime(_leftInventoryTime);
+ _leftAreaMovie.show();
+ _leftAreaMovie.redrawMovieWorld();
+ } else {
+ _rightAreaMovie.setTime(_rightBiochipTime);
+ _rightAreaMovie.show();
+ _rightAreaMovie.redrawMovieWorld();
+ }
+ }
+
+ vm->_cursor->hideUntilMoved();
+ unlockAI();
+ return result;
+}
+
+// Only implemented for kMiddleAreaSignature, kInventorySignature
+void AIArea::loopAIAreaSequence(const LowerClientSignature owner, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) {
+ if (area == kMiddleAreaSignature && owner == kInventorySignature && owner == _middleAreaOwner) {
+ _middleAreaMovie.stop();
+ _middleAreaMovie.setFlags(0);
+ _middleAreaMovie.setSegment(start, stop);
+ _middleAreaMovie.setFlags(kLoopTimeBase);
+ _middleAreaMovie.setTime(start);
+ _middleAreaMovie.show();
+ _middleAreaMovie.start();
+ }
+}
+
+// Only called by kInventorySignature.
+void AIArea::setLeftMovieTime(const TimeValue time) {
+ if (!_AIMovie.isSurfaceValid()) {
+ _leftAreaMovie.setTime(time);
+ _leftAreaMovie.show();
+ _leftAreaMovie.redrawMovieWorld();
+ }
+
+ _leftAreaOwner = kInventorySignature;
+ _leftInventoryTime = time;
+}
+
+void AIArea::setMiddleMovieTime(const LowerClientSignature client, const TimeValue time) {
+ if (client == kInventorySignature) {
+ _middleInventoryTime = time;
+ if (_middleAreaOwner == kBiochipSignature) {
+ BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+ if (currentBiochip && currentBiochip->isSelected())
+ currentBiochip->giveUpSharedArea();
+ }
+ } else {
+ _middleBiochipTime = time;
+ if (_middleAreaOwner == kInventorySignature) {
+ InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem();
+ if (currentItem && currentItem->isSelected())
+ currentItem->giveUpSharedArea();
+ }
+ }
+
+ _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration());
+ _middleAreaMovie.stop();
+ _middleAreaMovie.setFlags(0);
+ _middleAreaMovie.setTime(time);
+ _middleAreaMovie.show();
+ _middleAreaMovie.redrawMovieWorld();
+ _middleAreaOwner = client;
+}
+
+// Only called by kBiochipSignature.
+void AIArea::setRightMovieTime(const TimeValue time) {
+ if (!_AIMovie.isSurfaceValid()) {
+ // Can't do it when the AI movie is up...
+ _rightAreaMovie.setTime(time);
+ _rightAreaMovie.show();
+ _rightAreaMovie.redrawMovieWorld();
+ }
+
+ _rightAreaOwner = kBiochipSignature;
+ _rightBiochipTime = time;
+}
+
+void AIArea::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (JMPPPInput::isToggleAIMiddleInput(input))
+ toggleMiddleAreaOwner();
+ else
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+void AIArea::toggleMiddleAreaOwner() {
+ if (_middleAreaOwner == kInventorySignature) {
+ BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+ if (currentBiochip) {
+ setMiddleMovieTime(kBiochipSignature, currentBiochip->getSharedAreaTime());
+ currentBiochip->takeSharedArea();
+ }
+ } else if (_middleAreaOwner == kBiochipSignature) {
+ InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem();
+ if (currentItem) {
+ setMiddleMovieTime(kInventorySignature, currentItem->getSharedAreaTime());
+ currentItem->takeSharedArea();
+ }
+ }
+}
+
+void AIArea::activateHotspots() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ if (_middleAreaOwner == kBiochipSignature) {
+ BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+ if (currentBiochip)
+ switch (currentBiochip->getObjectID()) {
+ case kAIBiochip:
+ ((AIChip *)currentBiochip)->activateAIHotspots();
+ break;
+ case kPegasusBiochip:
+ if (!vm->isDemo())
+ ((PegasusChip *)currentBiochip)->activatePegasusHotspots();
+ break;
+ case kOpticalBiochip:
+ ((OpticalChip *)currentBiochip)->activateOpticalHotspots();
+ break;
+ }
+ } else if (_middleAreaOwner == kInventorySignature) {
+ InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem();
+ if (currentItem && currentItem->getObjectID() == kAirMask)
+ ((AirMask *)currentItem)->activateAirMaskHotspots();
+ }
+
+ InputHandler::activateHotspots();
+}
+
+void AIArea::clickInHotspot(const Input &input, const Hotspot *hotspot) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ bool handled = false;
+
+ if (_middleAreaOwner == kBiochipSignature) {
+ BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+
+ if (currentBiochip) {
+ switch (currentBiochip->getObjectID()) {
+ case kAIBiochip:
+ if ((hotspot->getHotspotFlags() & kAIBiochipSpotFlag) != 0) {
+ ((AIChip *)currentBiochip)->clickInAIHotspot(hotspot->getObjectID());
+ handled = true;
+ }
+ break;
+ case kPegasusBiochip:
+ if (!vm->isDemo() && ((hotspot->getHotspotFlags() & kPegasusBiochipSpotFlag) != 0)) {
+ ((PegasusChip *)currentBiochip)->clickInPegasusHotspot();
+ handled = true;
+ }
+ break;
+ case kOpticalBiochip:
+ if ((hotspot->getHotspotFlags() & kOpticalBiochipSpotFlag) != 0) {
+ ((OpticalChip *)currentBiochip)->clickInOpticalHotspot(hotspot->getObjectID());
+ handled = true;
+ }
+ break;
+ }
+ }
+ } else if (_middleAreaOwner == kInventorySignature) {
+ InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem();
+
+ if (currentItem) {
+ switch (currentItem->getObjectID()) {
+ case kAirMask:
+ if ((hotspot->getHotspotFlags() & kAirMaskSpotFlag) != 0) {
+ ((AirMask *)currentItem)->clickInAirMaskHotspot();
+ handled = true;
+ }
+ break;
+ }
+ }
+ }
+
+ if (!handled)
+ InputHandler::clickInHotspot(input, hotspot);
+}
+
+void AIArea::lockAIOut() {
+ if (_lockCount == 0)
+ stopIdling();
+
+ _lockCount++;
+}
+
+void AIArea::unlockAI() {
+ if (_lockCount > 0) {
+ _lockCount--;
+ if (_lockCount == 0)
+ startIdling();
+ }
+}
+
+void AIArea::forceAIUnlocked() {
+ if (_lockCount > 0) {
+ _lockCount = 1;
+ unlockAI();
+ }
+}
+
+void AIArea::checkRules() {
+ if (_lockCount == 0 && ((PegasusEngine *)g_engine)->playerAlive())
+ for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++)
+ if ((*it)->fireRule())
+ break;
+}
+
+void AIArea::useIdleTime() {
+ checkRules();
+}
+
+void AIArea::addAIRule(AIRule *rule) {
+ _AIRules.push_back(rule);
+}
+
+void AIArea::removeAllRules() {
+ for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++)
+ delete *it;
+
+ _AIRules.clear();
+}
+
+void AIArea::checkMiddleArea() {
+ BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+
+ if (currentBiochip) {
+ if (_middleAreaOwner == kBiochipSignature) {
+ switch (currentBiochip->getObjectID()) {
+ case kAIBiochip:
+ ((AIChip *)currentBiochip)->setUpAIChip();
+ break;
+ case kPegasusBiochip:
+ ((PegasusChip *)currentBiochip)->setUpPegasusChip();
+ break;
+ }
+ } else {
+ switch (currentBiochip->getObjectID()) {
+ case kAIBiochip:
+ ((AIChip *)currentBiochip)->setUpAIChipRude();
+ break;
+ case kPegasusBiochip:
+ ((PegasusChip *)currentBiochip)->setUpPegasusChipRude();
+ break;
+ }
+ }
+ }
+}
+
+TimeValue AIArea::getBigInfoTime() {
+ if (_middleAreaOwner == kInventorySignature) {
+ InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem();
+ return currentItem->getInfoLeftTime();
+ } else if (_middleAreaOwner == kBiochipSignature) {
+ BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+ return currentBiochip->getInfoLeftTime();
+ }
+
+ return 0xffffffff;
+}
+
+void AIArea::getSmallInfoSegment(TimeValue &start, TimeValue &stop) {
+ if (_middleAreaOwner == kInventorySignature) {
+ InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem();
+ currentItem->getInfoRightTimes(start, stop);
+ } else if (_middleAreaOwner == kBiochipSignature) {
+ BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+ currentBiochip->getInfoRightTimes(start, stop);
+ } else {
+ start = 0xffffffff;
+ stop = 0xffffffff;
+ }
+}
+
+LowerClientSignature AIArea::getMiddleAreaOwner() {
+ return _middleAreaOwner;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/ai/ai_area.h b/engines/pegasus/ai/ai_area.h
new file mode 100644
index 0000000000..806e6ef6bb
--- /dev/null
+++ b/engines/pegasus/ai/ai_area.h
@@ -0,0 +1,172 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_AI_AIAREA_H
+#define PEGASUS_AI_AIAREA_H
+
+#include "pegasus/input.h"
+#include "pegasus/movie.h"
+#include "pegasus/timers.h"
+#include "pegasus/ai/ai_rule.h"
+
+namespace Common {
+ class ReadStream;
+ class WriteStream;
+}
+
+/*
+
+ The AI area is the area at the bottom of the screen. There are three areas within
+ the AI area:
+ 1) the inventory/AI help area
+ 2) the middle area
+ 3) the biochip display area
+
+ Area 1 is used for displaying the current inventory item. When the player changes the
+ current item, either by selecting a new one in the inventory list or by picking
+ up a new item, area 1 updates to show the new item.
+
+ If the AI decides to give a message, the AI's head temporarily takes over area 1
+ for the duration of the message, then goes away, returning the area to the current
+ inventory item.
+
+ Area 2 is used to display the current inventory item's state, the current biochip's
+ state, and any extra information from the AI. The contention for this area is
+ resolved as follows:
+ -- If the AI needs to use the area while giving a message in area 1, it takes over
+ area 2 for the duration of its message.
+*** This is not true.
+ -- If the player selects a new inventory item, the inventory item's state gets
+ displayed immediately.
+ -- If the player selects a new biochip, the biochip's state info gets displayed
+ immediately.
+ -- If any auto drawing is to occur, it seizes the area as soon as the drawing is
+ to occur. For example, the mapping biochip does auto drawing every time the
+ player takes a step. The only exception to this rule is if the AI is presenting
+ a warning. When the AI seizes areas 1 and 2, it preempts all other uses.
+ Some inventory items and biochips can cause arbitrary drawing to occur in this area
+ at arbitrary times. The map biochip is one example which causes drawing when the
+ player takes a step. Another example is the poison gas canister, which flashes in
+ this area to indicate a dangerous compound.
+
+ Area 3 is used to display the current biochip. When the player changes the current
+ biochip, either by selecting a new one from the biochip list or by picking up a
+ new one, area 3 updates to show the new item. In addition, some biochips can play
+ animation in this area.
+
+*/
+
+namespace Pegasus {
+
+class AIArea : public Surface, public Idler, public InputHandler {
+public:
+ AIArea(InputHandler *);
+ virtual ~AIArea();
+
+ void writeAIRules(Common::WriteStream *stream);
+ void readAIRules(Common::ReadStream *stream);
+
+ void initAIArea();
+
+ void saveAIState();
+ void restoreAIState();
+
+ void handleInput(const Input &, const Hotspot *);
+ void activateHotspots();
+ void clickInHotspot(const Input &, const Hotspot *);
+
+ void setAIVolume(const uint16);
+
+ // There are only so many legal combinations of client/area.
+ // Here is the list of supported pairs:
+ // kInventorySignature kLeftAreaSignature
+ // kInventorySignature kMiddleAreaSignature
+ // kBiochipSignature kMiddleAreaSignature
+ // kBiochipSignature kRightAreaSignature
+ // kAISignature kLeftAreaSignature
+ // Further, the kAISignature never sets a static frame time in the left area,
+ // but only plays a sequence from the AI movie.
+ void setAIAreaToTime(const LowerClientSignature, const LowerAreaSignature, const TimeValue);
+
+ // The "Play" functions play the requested sequence synchronously.
+ void playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature, const TimeValue, const TimeValue);
+
+ // For PlayAIMovie, it is assumed that the client is the AI itself.
+ // This is used to play AI messages as well as Optical Memory video.
+ // Returns true if the movie played all the way through, false if it was interrupted.
+ bool playAIMovie(const LowerAreaSignature, const Common::String &movieName, bool keepLastFrame, const InputBits);
+
+ // Loop the requested sequence indefinitely.
+ void loopAIAreaSequence(const LowerClientSignature, const LowerAreaSignature, const TimeValue, const TimeValue);
+
+ void addAIRule(AIRule *);
+
+ // Remove and delete all rules.
+ void removeAllRules();
+
+ void lockAIOut();
+ void unlockAI();
+ void forceAIUnlocked();
+
+ void checkMiddleArea();
+ void checkRules();
+
+ LowerClientSignature getMiddleAreaOwner();
+ void toggleMiddleAreaOwner();
+
+ TimeValue getBigInfoTime();
+ void getSmallInfoSegment(TimeValue &, TimeValue &);
+
+protected:
+ void useIdleTime();
+
+ void setLeftMovieTime(const TimeValue);
+ void setMiddleMovieTime(const LowerClientSignature, const TimeValue);
+ void setRightMovieTime(const TimeValue);
+
+ Movie _leftAreaMovie;
+ Movie _middleAreaMovie;
+ Movie _rightAreaMovie;
+ Movie _AIMovie;
+
+ LowerClientSignature _leftAreaOwner;
+ LowerClientSignature _middleAreaOwner;
+ LowerClientSignature _rightAreaOwner;
+
+ TimeValue _leftInventoryTime;
+ TimeValue _middleInventoryTime;
+ TimeValue _middleBiochipTime;
+ TimeValue _rightBiochipTime;
+
+ AIRuleList _AIRules;
+
+ uint _lockCount;
+};
+
+extern AIArea *g_AIArea;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/ai/ai_condition.cpp b/engines/pegasus/ai/ai_condition.cpp
new file mode 100644
index 0000000000..9fc9272566
--- /dev/null
+++ b/engines/pegasus/ai/ai_condition.cpp
@@ -0,0 +1,290 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/stream.h"
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_condition.h"
+#include "pegasus/items/itemlist.h"
+#include "pegasus/items/biochips/biochipitem.h"
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+AIOneChildCondition::AIOneChildCondition(AICondition *child) {
+ _child = child;
+}
+
+AIOneChildCondition::~AIOneChildCondition() {
+ delete _child;
+}
+
+void AIOneChildCondition::writeAICondition(Common::WriteStream *stream) {
+ if (_child)
+ _child->writeAICondition(stream);
+}
+
+void AIOneChildCondition::readAICondition(Common::ReadStream *stream) {
+ if (_child)
+ _child->readAICondition(stream);
+}
+
+AITwoChildrenCondition::AITwoChildrenCondition(AICondition *leftChild, AICondition *rightChild) {
+ _leftChild = leftChild;
+ _rightChild = rightChild;
+}
+
+AITwoChildrenCondition::~AITwoChildrenCondition() {
+ delete _leftChild;
+ delete _rightChild;
+}
+
+void AITwoChildrenCondition::writeAICondition(Common::WriteStream *stream) {
+ if (_leftChild)
+ _leftChild->writeAICondition(stream);
+
+ if (_rightChild)
+ _rightChild->writeAICondition(stream);
+}
+
+void AITwoChildrenCondition::readAICondition(Common::ReadStream *stream) {
+ if (_leftChild)
+ _leftChild->readAICondition(stream);
+
+ if (_rightChild)
+ _rightChild->readAICondition(stream);
+}
+
+AINotCondition::AINotCondition(AICondition* child) : AIOneChildCondition(child) {
+}
+
+bool AINotCondition::fireCondition() {
+ return _child && !_child->fireCondition();
+}
+
+AIAndCondition::AIAndCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) {
+}
+
+bool AIAndCondition::fireCondition() {
+ return _leftChild && _leftChild->fireCondition() && _rightChild && _rightChild->fireCondition();
+}
+
+AIOrCondition::AIOrCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) {
+}
+
+bool AIOrCondition::fireCondition() {
+ return (_leftChild && _leftChild->fireCondition()) || (_rightChild && _rightChild->fireCondition());
+}
+
+AITimerCondition::AITimerCondition(const TimeValue time, const TimeScale scale, const bool shouldStartTimer) {
+ _timerFuse.primeFuse(time, scale);
+ _timerFuse.setFunctor(new Common::Functor0Mem<void, AITimerCondition>(this, &AITimerCondition::fire));
+ _fired = false;
+
+ if (shouldStartTimer)
+ startTimer();
+}
+
+void AITimerCondition::startTimer() {
+ _fired = false;
+ _timerFuse.lightFuse();
+}
+
+void AITimerCondition::stopTimer() {
+ _timerFuse.stopFuse();
+}
+
+void AITimerCondition::writeAICondition(Common::WriteStream *stream) {
+ stream->writeByte(_timerFuse.isFuseLit());
+ stream->writeByte(_fired);
+ stream->writeUint32BE(_timerFuse.getTimeRemaining());
+ stream->writeUint32BE(_timerFuse.getFuseScale());
+}
+
+void AITimerCondition::readAICondition(Common::ReadStream *stream) {
+ bool running = stream->readByte();
+ _fired = stream->readByte();
+ TimeValue time = stream->readUint32BE();
+ TimeScale scale = stream->readUint32BE();
+
+ _timerFuse.stopFuse();
+ _timerFuse.primeFuse(time, scale);
+
+ if (running)
+ _timerFuse.lightFuse();
+}
+
+bool AITimerCondition::fireCondition() {
+ return _fired;
+}
+
+void AITimerCondition::fire() {
+ _fired = true;
+}
+
+AILocationCondition::AILocationCondition(uint32 maxLocations) {
+ _numLocations = 0;
+ _maxLocations = maxLocations;
+ _locations = new RoomViewID[maxLocations];
+}
+
+AILocationCondition::~AILocationCondition() {
+ delete[] _locations;
+}
+
+void AILocationCondition::addLocation(const RoomViewID location) {
+ if (_numLocations < _maxLocations)
+ _locations[_numLocations++] = location;
+}
+
+bool AILocationCondition::fireCondition() {
+ RoomViewID test = GameState.getCurrentRoomAndView(), *p;
+ uint32 i;
+
+ for (i = 0, p = _locations; i < _numLocations; i++, p++) {
+ if (test == *p) {
+ *p = MakeRoomView(kNoRoomID, kNoDirection);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AILocationCondition::writeAICondition(Common::WriteStream *stream) {
+ stream->writeUint32BE(_maxLocations);
+ stream->writeUint32BE(_numLocations);
+
+ uint32 i;
+ RoomViewID *p;
+ for (i = 0, p = _locations; i < _numLocations; i++, p++)
+ stream->writeUint32BE(*p);
+}
+
+void AILocationCondition::readAICondition(Common::ReadStream *stream) {
+ uint32 maxLocations = stream->readUint32BE();
+
+ if (_maxLocations != maxLocations) {
+ delete[] _locations;
+ _locations = new RoomViewID[maxLocations];
+ _maxLocations = maxLocations;
+ }
+
+ _numLocations = stream->readUint32BE();
+
+ uint32 i;
+ RoomViewID *p;
+ for (i = 0, p = _locations; i < _numLocations; i++, p++)
+ *p = stream->readUint32BE();
+}
+
+AIDoorOpenedCondition::AIDoorOpenedCondition(RoomViewID doorLocation) {
+ _doorLocation = doorLocation;
+}
+
+bool AIDoorOpenedCondition::fireCondition() {
+ return GameState.getCurrentRoomAndView() == _doorLocation && GameState.isCurrentDoorOpen();
+}
+
+AIHasItemCondition::AIHasItemCondition(const ItemID item) {
+ _item = item;
+}
+
+bool AIHasItemCondition::fireCondition() {
+ return _item == kNoItemID || GameState.isTakenItemID(_item);
+}
+
+AIDoesntHaveItemCondition::AIDoesntHaveItemCondition(const ItemID item) {
+ _item = item;
+}
+
+bool AIDoesntHaveItemCondition::fireCondition() {
+ return _item == kNoItemID || !GameState.isTakenItemID(_item);
+}
+
+AICurrentItemCondition::AICurrentItemCondition(const ItemID item) {
+ _item = item;
+}
+
+bool AICurrentItemCondition::fireCondition() {
+ InventoryItem *item = ((PegasusEngine *)g_engine)->getCurrentInventoryItem();
+
+ if (_item == kNoItemID)
+ return item == 0;
+
+ return item != 0 && item->getObjectID() == _item;
+}
+
+AICurrentBiochipCondition::AICurrentBiochipCondition(const ItemID biochip) {
+ _biochip = biochip;
+}
+
+bool AICurrentBiochipCondition::fireCondition() {
+ BiochipItem *biochip = ((PegasusEngine *)g_engine)->getCurrentBiochip();
+
+ if (_biochip == kNoItemID)
+ return biochip == 0;
+
+ return biochip != 0 && biochip->getObjectID() == _biochip;
+}
+
+AIItemStateCondition::AIItemStateCondition(const ItemID item, const ItemState state) {
+ _item = item;
+ _state = state;
+}
+
+bool AIItemStateCondition::fireCondition() {
+ Item *item = g_allItems.findItemByID(_item);
+ return item != 0 && item->getItemState() == _state;
+}
+
+AIEnergyMonitorCondition::AIEnergyMonitorCondition(const int32 energyThreshold) {
+ _energyThreshold = energyThreshold;
+}
+
+bool AIEnergyMonitorCondition::fireCondition() {
+ return g_energyMonitor != 0 && g_energyMonitor->getCurrentEnergy() < _energyThreshold;
+}
+
+AILastExtraCondition::AILastExtraCondition(const ExtraID lastExtra) {
+ _lastExtra = lastExtra;
+}
+
+bool AILastExtraCondition::fireCondition() {
+ return g_neighborhood && (ExtraID)g_neighborhood->getLastExtra() == _lastExtra;
+}
+
+AICondition *makeLocationAndDoesntHaveItemCondition(const RoomID room, const DirectionConstant direction, const ItemID item) {
+ AILocationCondition *location = new AILocationCondition(1);
+ location->addLocation(MakeRoomView(room, direction));
+
+ AIDoesntHaveItemCondition *doesntHaveItem = new AIDoesntHaveItemCondition(item);
+
+ return new AIAndCondition(location, doesntHaveItem);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/ai/ai_condition.h b/engines/pegasus/ai/ai_condition.h
new file mode 100644
index 0000000000..ae85168a36
--- /dev/null
+++ b/engines/pegasus/ai/ai_condition.h
@@ -0,0 +1,287 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_AI_AICONDITION_H
+#define PEGASUS_AI_AICONDITION_H
+
+#include "pegasus/timers.h"
+
+namespace Common {
+ class ReadStream;
+ class WriteStream;
+}
+
+namespace Pegasus {
+
+/////////////////////////////////////////////
+//
+// AICondition
+
+class AICondition {
+public:
+ AICondition() {}
+ virtual ~AICondition() {}
+
+ virtual bool fireCondition() = 0;
+
+ // Only need these for conditions that are dynamic, like timer conditions...
+ // other conditions, like item related conditions, which don't change during
+ // the run of an environment, are completely initted when the environment
+ // is created.
+ virtual void writeAICondition(Common::WriteStream *) {}
+ virtual void readAICondition(Common::ReadStream *) {}
+};
+
+/////////////////////////////////////////////
+//
+// AIOneChildCondition
+
+class AIOneChildCondition : public AICondition {
+public:
+ AIOneChildCondition(AICondition *);
+ virtual ~AIOneChildCondition();
+
+ virtual void writeAICondition(Common::WriteStream *);
+ virtual void readAICondition(Common::ReadStream *);
+
+protected:
+ AICondition *_child;
+};
+
+/////////////////////////////////////////////
+//
+// AITwoChildrenCondition
+
+class AITwoChildrenCondition : public AICondition {
+public:
+ AITwoChildrenCondition(AICondition *, AICondition *);
+ virtual ~AITwoChildrenCondition();
+
+ virtual void writeAICondition(Common::WriteStream *);
+ virtual void readAICondition(Common::ReadStream *);
+
+protected:
+ AICondition *_leftChild, *_rightChild;
+};
+
+/////////////////////////////////////////////
+//
+// AINotCondition
+
+class AINotCondition : public AIOneChildCondition {
+public:
+ AINotCondition(AICondition *);
+
+ virtual bool fireCondition();
+};
+
+/////////////////////////////////////////////
+//
+// AIAndCondition
+
+class AIAndCondition : public AITwoChildrenCondition {
+public:
+ AIAndCondition(AICondition *, AICondition *);
+
+ virtual bool fireCondition();
+};
+
+/////////////////////////////////////////////
+//
+// AIOrCondition
+
+class AIOrCondition : public AITwoChildrenCondition {
+public:
+ AIOrCondition(AICondition *, AICondition *);
+
+ virtual bool fireCondition();
+};
+
+/////////////////////////////////////////////
+//
+// AITimerCondition
+
+class AITimerCondition : public AICondition {
+public:
+ AITimerCondition(const TimeValue, const TimeScale, const bool);
+
+ void startTimer();
+ void stopTimer();
+
+ virtual bool fireCondition();
+
+ virtual void writeAICondition(Common::WriteStream *);
+ virtual void readAICondition(Common::ReadStream *);
+
+protected:
+ void fire();
+
+ FuseFunction _timerFuse;
+ bool _fired;
+};
+
+/////////////////////////////////////////////
+//
+// AILocationCondition
+
+class AILocationCondition : public AICondition {
+public:
+ AILocationCondition(uint32);
+ virtual ~AILocationCondition();
+
+ void addLocation(RoomViewID);
+ virtual bool fireCondition();
+
+ virtual void writeAICondition(Common::WriteStream *);
+ virtual void readAICondition(Common::ReadStream *);
+
+protected:
+ uint32 _numLocations, _maxLocations;
+ RoomViewID *_locations;
+};
+
+/////////////////////////////////////////////
+//
+// AIDoorOpenedCondition
+
+class AIDoorOpenedCondition : public AICondition {
+public:
+ AIDoorOpenedCondition(RoomViewID);
+ virtual ~AIDoorOpenedCondition() {}
+
+ virtual bool fireCondition();
+
+protected:
+ RoomViewID _doorLocation;
+};
+
+/////////////////////////////////////////////
+//
+// AIHasItemCondition
+
+class AIHasItemCondition : public AICondition {
+public:
+ AIHasItemCondition(const ItemID);
+
+ virtual bool fireCondition();
+
+protected:
+ ItemID _item;
+};
+
+/////////////////////////////////////////////
+//
+// AIDoesntHaveItemCondition
+
+class AIDoesntHaveItemCondition : public AICondition {
+public:
+ AIDoesntHaveItemCondition(const ItemID);
+
+ virtual bool fireCondition();
+
+protected:
+ ItemID _item;
+};
+
+/////////////////////////////////////////////
+//
+// AICurrentItemCondition
+
+class AICurrentItemCondition : public AICondition {
+public:
+ AICurrentItemCondition(const ItemID);
+
+ virtual bool fireCondition();
+
+protected:
+ ItemID _item;
+};
+
+/////////////////////////////////////////////
+//
+// AICurrentBiochipCondition
+
+class AICurrentBiochipCondition : public AICondition {
+public:
+ AICurrentBiochipCondition(const ItemID);
+
+ virtual bool fireCondition();
+
+protected:
+ ItemID _biochip;
+};
+
+/////////////////////////////////////////////
+//
+// AIItemStateCondition
+
+class AIItemStateCondition : public AICondition {
+public:
+ AIItemStateCondition(const ItemID, const ItemState);
+
+ virtual bool fireCondition();
+
+protected:
+ ItemID _item;
+ ItemState _state;
+};
+
+/////////////////////////////////////////////
+//
+// AIEnergyMonitorCondition
+
+class AIEnergyMonitorCondition : public AICondition {
+public:
+ AIEnergyMonitorCondition(const int32);
+
+ virtual bool fireCondition();
+
+protected:
+ int32 _energyThreshold;
+};
+
+/////////////////////////////////////////////
+//
+// AILastExtraCondition
+
+class AILastExtraCondition : public AICondition {
+public:
+ AILastExtraCondition(const ExtraID);
+
+ virtual bool fireCondition();
+
+protected:
+ ExtraID _lastExtra;
+};
+
+/////////////////////////////////////////////
+//
+// Helper functions
+
+AICondition *makeLocationAndDoesntHaveItemCondition(const RoomID room, const DirectionConstant direction, const ItemID item);
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/ai/ai_rule.cpp b/engines/pegasus/ai/ai_rule.cpp
new file mode 100644
index 0000000000..3aaa530a4a
--- /dev/null
+++ b/engines/pegasus/ai/ai_rule.cpp
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/stream.h"
+
+#include "pegasus/ai/ai_action.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/ai/ai_condition.h"
+#include "pegasus/ai/ai_rule.h"
+
+namespace Pegasus {
+
+bool AIRule::fireRule() {
+ if (_ruleActive && _ruleCondition && _ruleAction && _ruleCondition->fireCondition()) {
+ if (g_AIArea)
+ g_AIArea->lockAIOut();
+
+ _ruleAction->performAIAction(this);
+
+ if (--_ruleAction->_actionCount <= 0)
+ deactivateRule();
+
+ if (g_AIArea)
+ g_AIArea->unlockAI();
+
+ return true;
+ }
+
+ return false;
+}
+
+void AIRule::writeAIRule(Common::WriteStream *stream) {
+ stream->writeByte(_ruleActive);
+
+ if (_ruleCondition)
+ _ruleCondition->writeAICondition(stream);
+}
+
+void AIRule::readAIRule(Common::ReadStream *stream) {
+ _ruleActive = stream->readByte();
+
+ if (_ruleCondition)
+ _ruleCondition->readAICondition(stream);
+}
+
+void AIRuleList::writeAIRules(Common::WriteStream *stream) {
+ for (AIRuleList::iterator it = begin(); it != end(); it++)
+ (*it)->writeAIRule(stream);
+}
+
+void AIRuleList::readAIRules(Common::ReadStream *stream) {
+ for (AIRuleList::iterator it = begin(); it != end(); it++)
+ (*it)->readAIRule(stream);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/ai/ai_rule.h b/engines/pegasus/ai/ai_rule.h
new file mode 100644
index 0000000000..aa2ca07332
--- /dev/null
+++ b/engines/pegasus/ai/ai_rule.h
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_AI_AIRULE_H
+#define PEGASUS_AI_AIRULE_H
+
+#include "common/list.h"
+
+#include "pegasus/ai/ai_action.h"
+#include "pegasus/ai/ai_condition.h"
+
+namespace Common {
+ class ReadStream;
+ class WriteStream;
+}
+
+namespace Pegasus {
+
+class AICondition;
+class AIAction;
+
+class AIRule {
+public:
+ AIRule(AICondition *condition, AIAction *rule) {
+ _ruleCondition = condition;
+ _ruleAction = rule;
+ _ruleActive = true;
+ }
+
+ ~AIRule() {
+ if (_ruleCondition)
+ delete _ruleCondition;
+
+ if (_ruleAction)
+ delete _ruleAction;
+ }
+
+ bool fireRule();
+
+ void activateRule() { _ruleActive = true; }
+ void deactivateRule() { _ruleActive = false; }
+ bool isRuleActive() { return _ruleActive; }
+
+ void writeAIRule(Common::WriteStream *);
+ void readAIRule(Common::ReadStream *);
+
+protected:
+ AICondition *_ruleCondition;
+ AIAction *_ruleAction;
+ bool _ruleActive;
+};
+
+class AIRuleList : public Common::List<AIRule *> {
+public:
+ AIRuleList() {}
+ ~AIRuleList() {}
+
+ void writeAIRules(Common::WriteStream *);
+ void readAIRules(Common::ReadStream *);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/compass.cpp b/engines/pegasus/compass.cpp
new file mode 100644
index 0000000000..5de8b91b11
--- /dev/null
+++ b/engines/pegasus/compass.cpp
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/compass.h"
+
+namespace Pegasus {
+
+Compass *g_compass = 0;
+
+Compass::Compass() : FaderAnimation(kCompassID) {
+ // Initialize it to east...
+ setFaderValue(90);
+ g_compass = this;
+}
+
+Compass::~Compass() {
+ g_compass = 0;
+}
+
+void Compass::initCompass() {
+ if (!isCompassValid()) {
+ Common::Rect r;
+ _compassImage.initFromPICTFile("Images/Compass/Compass");
+ _compassImage.getSurfaceBounds(r);
+ r.right = kCompassWidth;
+ setBounds(r);
+ }
+}
+
+void Compass::deallocateCompass() {
+ _compassImage.deallocateSurface();
+}
+
+void Compass::setFaderValue(const int32 angle) {
+ int16 a = angle % 360;
+
+ if (a < 0)
+ a += 360;
+
+ FaderAnimation::setFaderValue(a);
+}
+
+void Compass::draw(const Common::Rect &r1) {
+ if (_compassImage.isSurfaceValid()) {
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ Common::Rect r2;
+ _compassImage.getSurfaceBounds(r2);
+
+ CoordType width = r2.width();
+ CoordType offsetH = width / 10 - bounds.width() / 2 + (getFaderValue() * width) / 450 - bounds.left;
+ CoordType offsetV = -bounds.top;
+ r2 = r1;
+ r2.translate(offsetH, offsetV);
+ _compassImage.drawImage(r2, r1);
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/compass.h b/engines/pegasus/compass.h
new file mode 100644
index 0000000000..67a8e06294
--- /dev/null
+++ b/engines/pegasus/compass.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_COMPASS_H
+#define PEGASUS_COMPASS_H
+
+#include "pegasus/fader.h"
+#include "pegasus/surface.h"
+
+namespace Pegasus {
+
+// Compass is defined with 0 as north, 90 east, 180 south, 270 west.
+// Clockwise rotation increases the angle, counterclockwise rotation decreases the angle.
+
+class Compass : public FaderAnimation {
+public:
+ Compass();
+ virtual ~Compass();
+
+ void initCompass();
+ void deallocateCompass();
+ bool isCompassValid() const { return _compassImage.isSurfaceValid(); }
+
+ void setFaderValue(const int32);
+
+protected:
+ void draw(const Common::Rect &);
+
+ Frame _compassImage;
+};
+
+extern Compass *g_compass;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/console.cpp b/engines/pegasus/console.cpp
new file mode 100644
index 0000000000..64bd0ba5f2
--- /dev/null
+++ b/engines/pegasus/console.cpp
@@ -0,0 +1,102 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/console.h"
+#include "pegasus/interface.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+PegasusConsole::PegasusConsole(PegasusEngine *vm) : GUI::Debugger(), _vm(vm) {
+ DCmd_Register("die", WRAP_METHOD(PegasusConsole, Cmd_Die));
+
+ // These functions are non-demo specific
+ if (!_vm->isDemo())
+ DCmd_Register("jump", WRAP_METHOD(PegasusConsole, Cmd_Jump));
+}
+
+PegasusConsole::~PegasusConsole() {
+}
+
+bool PegasusConsole::Cmd_Die(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: die <death reason>\n");
+ return true;
+ }
+
+ int reason = atoi(argv[1]);
+
+ bool invalidReason = (reason == 0 || reason > kPlayerWonGame);
+
+ if (!invalidReason && _vm->isDemo())
+ invalidReason = (reason != kDeathFallOffCliff) && (reason != kDeathEatenByDinosaur) &&
+ (reason != kDeathStranded) && (reason != kPlayerWonGame);
+
+
+ if (invalidReason) {
+ DebugPrintf("Invalid death reason %d\n", reason);
+ return true;
+ }
+
+ _vm->die(atoi(argv[1]));
+ return false;
+}
+
+bool PegasusConsole::Cmd_Jump(int argc, const char **argv) {
+ if (!g_interface) {
+ // TODO
+ DebugPrintf("Cannot jump without interface set up\n");
+ return true;
+ }
+
+ // TODO: Default room/direction for each neighborhood
+
+ if (argc < 4) {
+ DebugPrintf("Usage: jump <neighborhood> <room> <direction>\n");
+ return true;
+ }
+
+ NeighborhoodID neighborhood = (NeighborhoodID)atoi(argv[1]);
+ RoomID room = (RoomID)atoi(argv[2]);
+ DirectionConstant direction = (DirectionConstant)atoi(argv[3]);
+
+ if ((neighborhood < kCaldoriaID || neighborhood > kNoradDeltaID || neighborhood == kFinalTSAID) &&
+ neighborhood != kNoradSubChaseID) {
+ DebugPrintf("Invalid neighborhood %d", neighborhood);
+ return true;
+ }
+
+ // No real way to check room validity at this point
+
+ if (direction > kWest) {
+ DebugPrintf("Invalid direction %d", direction);
+ return true;
+ }
+
+ // Here we go!
+ // TODO: Can't clear menu since the engine is paused
+ _vm->jumpToNewEnvironment(neighborhood, room, direction);
+ return false;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/console.h b/engines/pegasus/console.h
new file mode 100644
index 0000000000..f00ee25294
--- /dev/null
+++ b/engines/pegasus/console.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_CONSOLE_H
+#define PEGASUS_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Pegasus {
+
+class PegasusEngine;
+
+class PegasusConsole : public GUI::Debugger {
+public:
+ PegasusConsole(PegasusEngine *vm);
+ virtual ~PegasusConsole();
+
+private:
+ bool Cmd_Die(int argc, const char **argv);
+ bool Cmd_Jump(int argc, const char **argv);
+
+ PegasusEngine *_vm;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/constants.h b/engines/pegasus/constants.h
new file mode 100644
index 0000000000..f81d2197ac
--- /dev/null
+++ b/engines/pegasus/constants.h
@@ -0,0 +1,729 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_CONSTANTS_H
+#define PEGASUS_CONSTANTS_H
+
+#include "common/endian.h"
+#include "common/rect.h"
+
+#include "pegasus/types.h"
+
+namespace Pegasus {
+
+// TODO: Organize these
+
+static const GameID kGameIDNothing = -1;
+
+static const ActorID kNoActorID = kGameIDNothing;
+static const ActorID kPlayerID = 0;
+static const ItemID kNoItemID = kGameIDNothing;
+static const RoomID kNoRoomID = kGameIDNothing;
+static const ExtraID kNoExtraID = 0xFFFFFFFF;
+static const NeighborhoodID kNoNeighborhoodID = kGameIDNothing;
+static const AlternateID kNoAlternateID = 0;
+static const GameMenuCommand kMenuCmdNoCommand = 0;
+
+static const HotSpotActivationID kActivateHotSpotAlways = 0;
+static const HotSpotActivationID kActivateHotSpotNever = -1;
+
+static const ItemState kNoItemState = -1;
+
+static const DirectionConstant kNoDirection = 0xFF;
+static const DirectionConstant kNorth = 0;
+static const DirectionConstant kSouth = 1;
+static const DirectionConstant kEast = 2;
+static const DirectionConstant kWest = 3;
+
+static const TurnDirection kNoTurn = 0xFF;
+static const TurnDirection kTurnLeft = 0;
+static const TurnDirection kTurnRight = 1;
+static const TurnDirection kTurnUp = 2;
+static const TurnDirection kTurnDown = 3;
+static const TurnDirection kMaxTurns = 4;
+
+static const GameMode kNoMode = -1;
+static const GameMode kModeNavigation = 0;
+static const GameMode kLastGameShellMode = kModeNavigation;
+
+static const CanMoveForwardReason kCanMoveForward = 0;
+static const CanMoveForwardReason kCantMoveBlocked = kCanMoveForward + 1;
+static const CanMoveForwardReason kCantMoveDoorClosed = kCantMoveBlocked + 1;
+static const CanMoveForwardReason kCantMoveDoorLocked = kCantMoveDoorClosed + 1;
+static const CanMoveForwardReason kCantMoveLastReason = kCantMoveDoorLocked;
+
+static const CanTurnReason kCanTurn = 0;
+static const CanTurnReason kCantTurnNoTurn = kCanTurn + 1;
+static const CanTurnReason kCantTurnLastReason = kCantTurnNoTurn;
+
+static const CanOpenDoorReason kCanOpenDoor = 0;
+static const CanOpenDoorReason kCantOpenNoDoor = kCanOpenDoor + 1;
+static const CanOpenDoorReason kCantOpenLocked = kCantOpenNoDoor + 1;
+static const CanOpenDoorReason kCantOpenAlreadyOpen = kCantOpenLocked + 1;
+static const CanOpenDoorReason kCantOpenLastReason = kCantOpenAlreadyOpen;
+
+static const DisplayElementID kNoDisplayElement = -1;
+static const DisplayElementID kHighestReservedElementID = -2;
+
+static const DisplayElementID kCursorID = kHighestReservedElementID;
+static const DisplayElementID kLoadScreenID = kCursorID - 1;
+
+static const DisplayOrder kMinAvailableOrder = 0;
+static const DisplayOrder kMaxAvailableOrder = 999998;
+static const DisplayOrder kLoadScreenOrder = 900000;
+static const DisplayOrder kCursorOrder = 1000000;
+
+static const HotSpotID kNoHotSpotID = -1;
+static const HotSpotFlags kNoHotSpotFlags = 0;
+static const HotSpotFlags kAllHotSpotFlags = ~kNoHotSpotFlags;
+
+static const NotificationFlags kNoNotificationFlags = 0;
+
+static const DisplayElementID kCurrentDragSpriteID = 1000;
+
+static const TimeScale kDefaultTimeScale = 600;
+
+// Ticks per second.
+
+static const TimeScale kOneTickPerSecond = 1;
+static const TimeScale kTwoTicksPerSecond = 2;
+static const TimeScale kFifteenTicksPerSecond = 15;
+static const TimeScale kThirtyTicksPerSecond = 30;
+static const TimeScale kSixtyTicksPerSecond = 60;
+static const TimeScale kMovieTicksPerSecond = 600;
+
+// These times are in seconds.
+
+static const TimeValue kOneSecond = 1;
+static const TimeValue kTwoSeconds = 2;
+static const TimeValue kThreeSeconds = 3;
+static const TimeValue kFourSeconds = 4;
+static const TimeValue kFiveSeconds = 5;
+static const TimeValue kSixSeconds = 6;
+static const TimeValue kSevenSeconds = 7;
+static const TimeValue kEightSeconds = 8;
+static const TimeValue kNineSeconds = 9;
+static const TimeValue kTenSeconds = 10;
+static const TimeValue kElevenSeconds = 11;
+static const TimeValue kTwelveSeconds = 12;
+static const TimeValue kThirteenSeconds = 13;
+static const TimeValue kFourteenSeconds = 14;
+static const TimeValue kFifteenSeconds = 15;
+static const TimeValue kSixteenSeconds = 16;
+static const TimeValue kSeventeenSeconds = 17;
+static const TimeValue kEighteenSeconds = 18;
+static const TimeValue kNineteenSeconds = 19;
+static const TimeValue kTwentySeconds = 20;
+static const TimeValue kThirtySeconds = 30;
+static const TimeValue kFortySeconds = 40;
+static const TimeValue kFiftySeconds = 50;
+static const TimeValue kSixtySeconds = 60;
+static const TimeValue kOneMinute = 60;
+static const TimeValue kTwoMinutes = kOneMinute * 2;
+static const TimeValue kThreeMinutes = kOneMinute * 3;
+static const TimeValue kFourMinutes = kOneMinute * 4;
+static const TimeValue kFiveMinutes = kOneMinute * 5;
+static const TimeValue kSixMinutes = kOneMinute * 6;
+static const TimeValue kSevenMinutes = kOneMinute * 7;
+static const TimeValue kEightMinutes = kOneMinute * 8;
+static const TimeValue kNineMinutes = kOneMinute * 9;
+static const TimeValue kTenMinutes = kOneMinute * 10;
+static const TimeValue kElevenMinutes = kOneMinute * 11;
+static const TimeValue kTwelveMinutes = kOneMinute * 12;
+static const TimeValue kThirteenMinutes = kOneMinute * 13;
+static const TimeValue kFourteenMinutes = kOneMinute * 14;
+static const TimeValue kFifteenMinutes = kOneMinute * 15;
+static const TimeValue kSixteenMinutes = kOneMinute * 16;
+static const TimeValue kSeventeenMinutes = kOneMinute * 17;
+static const TimeValue kEighteenMinutes = kOneMinute * 18;
+static const TimeValue kNineteenMinutes = kOneMinute * 19;
+static const TimeValue kTwentyMinutes = kOneMinute * 20;
+static const TimeValue kThirtyMinutes = kOneMinute * 30;
+static const TimeValue kFortyMinutes = kOneMinute * 40;
+static const TimeValue kFiftyMinutes = kOneMinute * 50;
+static const TimeValue kOneHour = kOneMinute * 60;
+static const TimeValue kTwoHours = kOneHour * 2;
+
+// Common times.
+
+static const TimeValue kHalfSecondPerTwoTicks = kTwoTicksPerSecond / 2;
+static const TimeValue kHalfSecondPerThirtyTicks = kThirtyTicksPerSecond / 2;
+static const TimeValue kHalfSecondPerSixtyTicks = kSixtyTicksPerSecond / 2;
+
+static const TimeValue kOneSecondPerTwoTicks = kTwoTicksPerSecond;
+static const TimeValue kOneSecondPerThirtyTicks = kThirtyTicksPerSecond;
+static const TimeValue kOneSecondPerSixtyTicks = kSixtyTicksPerSecond;
+
+static const TimeValue kOneMinutePerFifteenTicks = kOneMinute * kFifteenTicksPerSecond;
+static const TimeValue kFiveMinutesPerFifteenTicks = kFiveMinutes * kFifteenTicksPerSecond;
+static const TimeValue kTenMinutesPerFifteenTicks = kTenMinutes * kFifteenTicksPerSecond;
+
+static const TimeValue kOneMinutePerThirtyTicks = kOneMinute * kThirtyTicksPerSecond;
+static const TimeValue kFiveMinutesPerThirtyTicks = kFiveMinutes * kThirtyTicksPerSecond;
+static const TimeValue kTenMinutesPerThirtyTicks = kTenMinutes * kThirtyTicksPerSecond;
+
+static const TimeValue kOneMinutePerSixtyTicks = kOneMinute * kSixtyTicksPerSecond;
+static const TimeValue kFiveMinutesPerSixtyTicks = kFiveMinutes * kSixtyTicksPerSecond;
+static const TimeValue kTenMinutesPerSixtyTicks = kTenMinutes * kSixtyTicksPerSecond;
+
+// Time in seconds you can hang around Caldoria without going to work...
+static const TimeValue kLateWarning2TimeLimit = kFiveMinutes;
+static const TimeValue kLateWarning3TimeLimit = kTenMinutes;
+
+static const TimeValue kSinclairShootsTimeLimit = kThreeMinutes;
+static const TimeValue kCardBombCountDownTime = kTwelveSeconds;
+
+static const TimeValue kOxyMaskFullTime = kThirtyMinutes;
+
+static const TimeValue kTSAUncreatedTimeLimit = kFiveMinutes;
+static const TimeValue kRipTimeLimit = kTenMinutesPerFifteenTicks;
+static const TimeScale kRipTimeScale = kFifteenTicksPerSecond;
+
+static const TimeValue kIntroTimeOut = kThirtySeconds;
+
+static const TimeValue kMarsRobotPatienceLimit = kFifteenSeconds;
+static const TimeValue kLockFreezeTimeLmit = kFifteenSeconds;
+static const TimeValue kSpaceChaseTimeLimit = kTenMinutes;
+static const TimeValue kVacuumSurvivalTimeLimit = kThirtySeconds;
+static const TimeValue kColorMatchingTimeLimit = kFourMinutes;
+static const TimeScale kJunkTimeScale = kFifteenTicksPerSecond;
+static const TimeValue kJunkDropBaseTime = kFiveSeconds;
+static const TimeValue kJunkDropSlopTime = kThreeSeconds;
+static const TimeValue kJunkTravelTime = kTenSeconds * kJunkTimeScale;
+static const TimeValue kCollisionReboundTime = kOneSecond * kJunkTimeScale;
+static const TimeValue kWeaponReboundTime = kTwoSeconds * kJunkTimeScale;
+
+static const TimeValue kGawkAtRobotTime = kTenSeconds;
+static const TimeValue kGawkAtRobotTime2 = kThirteenSeconds;
+static const TimeValue kPlasmaImpactTime = kTwoSeconds;
+
+static const TimeValue kNoradAirMaskTimeLimit = kOneMinute + kFifteenSeconds;
+
+static const NotificationID kNeighborhoodNotificationID = 1;
+static const NotificationID kLastNeighborhoodNotificationID = kNeighborhoodNotificationID;
+
+static const NotificationFlags kNeighborhoodMovieCompletedFlag = 1;
+static const NotificationFlags kMoveForwardCompletedFlag = kNeighborhoodMovieCompletedFlag << 1;
+static const NotificationFlags kStrideCompletedFlag = kMoveForwardCompletedFlag << 1;
+static const NotificationFlags kTurnCompletedFlag = kStrideCompletedFlag << 1;
+static const NotificationFlags kSpotCompletedFlag = kTurnCompletedFlag << 1;
+static const NotificationFlags kDoorOpenCompletedFlag = kSpotCompletedFlag << 1;
+static const NotificationFlags kExtraCompletedFlag = kDoorOpenCompletedFlag << 1;
+static const NotificationFlags kSpotSoundCompletedFlag = kExtraCompletedFlag << 1;
+static const NotificationFlags kDelayCompletedFlag = kSpotSoundCompletedFlag << 1;
+static const NotificationFlags kActionRequestCompletedFlag = kDelayCompletedFlag << 1;
+static const NotificationFlags kDeathExtraCompletedFlag = kActionRequestCompletedFlag << 1;
+static const NotificationFlags kLastNeighborhoodNotificationFlag = kDeathExtraCompletedFlag;
+
+static const NotificationFlags kNeighborhoodFlags = kNeighborhoodMovieCompletedFlag |
+ kMoveForwardCompletedFlag |
+ kStrideCompletedFlag |
+ kTurnCompletedFlag |
+ kSpotCompletedFlag |
+ kDoorOpenCompletedFlag |
+ kExtraCompletedFlag |
+ kSpotSoundCompletedFlag |
+ kDelayCompletedFlag |
+ kActionRequestCompletedFlag |
+ kDeathExtraCompletedFlag;
+
+static const uint32 kPegasusPrimeCreator = MKTAG('J', 'P', 'P', 'P');
+static const uint32 kPegasusPrimeContinueType = MKTAG('P', 'P', 'C', 'T');
+
+static const uint32 kPegasusPrimeDisk1GameType = MKTAG('P', 'P', 'G', '1');
+static const uint32 kPegasusPrimeDisk2GameType = MKTAG('P', 'P', 'G', '2');
+static const uint32 kPegasusPrimeDisk3GameType = MKTAG('P', 'P', 'G', '3');
+static const uint32 kPegasusPrimeDisk4GameType = MKTAG('P', 'P', 'G', '4');
+
+// We only support one of the save versions; the rest are from betas
+// and we are not supporting them.
+static const uint32 kPegasusPrimeVersion = 0x00009019;
+
+static const char kNormalSave = 0;
+static const char kContinueSave = 1;
+
+// Display IDs.
+
+static const DisplayElementID kNavMovieID = 1;
+static const DisplayElementID kTurnPushID = 2;
+
+static const DisplayElementID kMaxGameShellDisplayID = kTurnPushID;
+
+// Display ordering.
+
+static const DisplayOrder kNavLayer = 10000;
+static const DisplayOrder kNavMovieOrder = kNavLayer;
+static const DisplayOrder kTurnPushOrder = kNavMovieOrder + 1;
+
+/////////////////////////////////////////////
+//
+// Display IDs.
+
+static const DisplayElementID kScreenDimmerID = kMaxGameShellDisplayID + 1;
+static const DisplayElementID kInterface1ID = kScreenDimmerID + 1;
+static const DisplayElementID kInterface2ID = kInterface1ID + 1;
+static const DisplayElementID kInterface3ID = kInterface2ID + 1;
+static const DisplayElementID kInterface4ID = kInterface3ID + 1;
+static const DisplayElementID kDateID = kInterface4ID + 1;
+static const DisplayElementID kCompassID = kDateID + 1;
+static const DisplayElementID kInventoryPushID = kCompassID + 1;
+static const DisplayElementID kInventoryLidID = kInventoryPushID + 1;
+static const DisplayElementID kBiochipPushID = kInventoryLidID + 1;
+static const DisplayElementID kBiochipLidID = kBiochipPushID + 1;
+static const DisplayElementID kEnergyBarID = kBiochipLidID + 1;
+static const DisplayElementID kWarningLightID = kEnergyBarID + 1;
+static const DisplayElementID kAILeftAreaID = kWarningLightID + 1;
+static const DisplayElementID kAIMiddleAreaID = kAILeftAreaID + 1;
+static const DisplayElementID kAIRightAreaID = kAIMiddleAreaID + 1;
+static const DisplayElementID kAIMovieID = kAIRightAreaID + 1;
+static const DisplayElementID kInventoryDropHighlightID = kAIMovieID + 1;
+static const DisplayElementID kBiochipDropHighlightID = kInventoryDropHighlightID + 1;
+
+static const DisplayElementID kDraggingSpriteID = 1000;
+
+static const DisplayElementID kCroppedMovieID = 2000;
+
+static const DisplayElementID kNeighborhoodDisplayID = 3000;
+
+static const DisplayElementID kItemPictureBaseID = 5000;
+
+static const CoordType kNavAreaLeft = 64;
+static const CoordType kNavAreaTop = 64;
+
+static const CoordType kBackground1Left = 0;
+static const CoordType kBackground1Top = 64;
+
+static const CoordType kBackground2Left = 0;
+static const CoordType kBackground2Top = 0;
+
+static const CoordType kBackground3Left = 576;
+static const CoordType kBackground3Top = 64;
+
+static const CoordType kBackground4Left = 0;
+static const CoordType kBackground4Top = 320;
+
+static const CoordType kOverviewControllerLeft = 540;
+static const CoordType kOverviewControllerTop = 348;
+
+static const CoordType kSwapLeft = 194;
+static const CoordType kSwapTop = 116;
+
+static const CoordType kSwapHiliteLeft = 200;
+static const CoordType kSwapHiliteTop = 206;
+
+static const CoordType kDateLeft = 136;
+static const CoordType kDateTop = 44;
+
+static const CoordType kCompassLeft = 222;
+static const CoordType kCompassTop = 42;
+static const CoordType kCompassWidth = 92;
+
+static const CoordType kInventoryPushLeft = 74;
+static const CoordType kInventoryPushTop = 92;
+
+static const CoordType kInventoryLidLeft = 74;
+static const CoordType kInventoryLidTop = 316;
+
+static const CoordType kBiochipPushLeft = 362;
+static const CoordType kBiochipPushTop = 192;
+
+static const CoordType kBiochipLidLeft = 362;
+static const CoordType kBiochipLidTop = 316;
+
+static const CoordType kInventoryDropLeft = 0;
+static const CoordType kInventoryDropTop = 320;
+static const CoordType kInventoryDropRight = 232;
+static const CoordType kInventoryDropBottom = 480;
+
+static const CoordType kBiochipDropLeft = 302;
+static const CoordType kBiochipDropTop = 320;
+static const CoordType kBiochipDropRight = 640;
+static const CoordType kBiochipDropBottom = 480;
+
+static const CoordType kFinalMessageLeft = kInventoryPushLeft + 1;
+static const CoordType kFinalMessageTop = kInventoryPushTop + 24;
+
+/////////////////////////////////////////////
+//
+// Notifications.
+
+static const NotificationID kJMPDCShellNotificationID = kLastNeighborhoodNotificationID + 1;
+static const NotificationID kInterfaceNotificationID = kJMPDCShellNotificationID + 1;
+static const NotificationID kAINotificationID = kInterfaceNotificationID + 1;
+static const NotificationID kNoradNotificationID = kAINotificationID + 1;
+static const NotificationID kNoradECRNotificationID = kNoradNotificationID + 1;
+static const NotificationID kNoradFillingStationNotificationID = kNoradECRNotificationID + 1;
+static const NotificationID kNoradPressureNotificationID = kNoradFillingStationNotificationID + 1;
+static const NotificationID kNoradUtilityNotificationID = kNoradPressureNotificationID + 1;
+static const NotificationID kNoradElevatorNotificationID = kNoradUtilityNotificationID + 1;
+static const NotificationID kNoradSubPlatformNotificationID = kNoradElevatorNotificationID + 1;
+static const NotificationID kSubControlNotificationID = kNoradSubPlatformNotificationID + 1;
+static const NotificationID kNoradGreenBallNotificationID = kSubControlNotificationID + 1;
+static const NotificationID kNoradGlobeNotificationID = kNoradGreenBallNotificationID + 1;
+static const NotificationID kCaldoriaVidPhoneNotificationID = kNoradGlobeNotificationID + 1;
+static const NotificationID kCaldoriaMessagesNotificationID = kCaldoriaVidPhoneNotificationID + 1;
+static const NotificationID kCaldoriaBombTimerNotificationID = kCaldoriaMessagesNotificationID + 1;
+
+// Sent to the shell by fShellNotification.
+static const NotificationFlags kGameStartingFlag = 1;
+static const NotificationFlags kNeedNewJumpFlag = kGameStartingFlag << 1;
+static const NotificationFlags kPlayerDiedFlag = kNeedNewJumpFlag << 1;
+
+static const NotificationFlags kJMPShellNotificationFlags = kGameStartingFlag |
+ kNeedNewJumpFlag |
+ kPlayerDiedFlag;
+
+// Sent to the interface.
+static const NotificationFlags kInventoryLidOpenFlag = 1;
+static const NotificationFlags kInventoryLidClosedFlag = kInventoryLidOpenFlag << 1;
+static const NotificationFlags kInventoryDrawerUpFlag = kInventoryLidClosedFlag << 1;
+static const NotificationFlags kInventoryDrawerDownFlag = kInventoryDrawerUpFlag << 1;
+static const NotificationFlags kBiochipLidOpenFlag = kInventoryDrawerDownFlag << 1;
+static const NotificationFlags kBiochipLidClosedFlag = kBiochipLidOpenFlag << 1;
+static const NotificationFlags kBiochipDrawerUpFlag = kBiochipLidClosedFlag << 1;
+static const NotificationFlags kBiochipDrawerDownFlag = kBiochipDrawerUpFlag << 1;
+
+static const NotificationFlags kInterfaceNotificationFlags = kInventoryLidOpenFlag |
+ kInventoryLidClosedFlag |
+ kInventoryDrawerUpFlag |
+ kInventoryDrawerDownFlag |
+ kBiochipLidOpenFlag |
+ kBiochipLidClosedFlag |
+ kBiochipDrawerUpFlag |
+ kBiochipDrawerDownFlag;
+
+// Hot spots.
+
+// Neighborhood hot spots.
+
+static const HotSpotID kFirstNeighborhoodSpotID = 5000;
+
+// kShellSpotFlag is a flag which marks all hot spots which belong to the shell, like
+// the current item and current biochip spots.
+static const HotSpotFlags kShellSpotFlag = 1;
+// kNeighborhoodSpotFlag is a flag which marks all hot spots which belong to a
+// neighborhood, like buttons on walls and so on.
+static const HotSpotFlags kNeighborhoodSpotFlag = kShellSpotFlag << 1;
+// kZoomInSpotFlag is a flag which marks all hot spots which indicate a zoom.
+static const HotSpotFlags kZoomInSpotFlag = kNeighborhoodSpotFlag << 1;
+// kZoomOutSpotFlag is a flag which marks all hot spots which indicate a zoom.
+static const HotSpotFlags kZoomOutSpotFlag = kZoomInSpotFlag << 1;
+
+static const HotSpotFlags kClickSpotFlag = kZoomOutSpotFlag << 1;
+static const HotSpotFlags kPlayExtraSpotFlag = kClickSpotFlag << 1;
+static const HotSpotFlags kPickUpItemSpotFlag = kPlayExtraSpotFlag << 1;
+static const HotSpotFlags kDropItemSpotFlag = kPickUpItemSpotFlag << 1;
+static const HotSpotFlags kOpenDoorSpotFlag = kDropItemSpotFlag << 1;
+
+static const HotSpotFlags kZoomSpotFlags = kZoomInSpotFlag | kZoomOutSpotFlag;
+
+static const HotSpotFlags kHighestGameShellSpotFlag = kOpenDoorSpotFlag;
+
+/////////////////////////////////////////////
+//
+// Hot spots.
+
+// Shell hot spots.
+// The shell reserves all hot spot IDs from 0 to 999
+
+static const HotSpotID kCurrentItemSpotID = 0;
+static const HotSpotID kCurrentBiochipSpotID = kCurrentItemSpotID + 1;
+
+static const HotSpotID kInventoryDropSpotID = kCurrentBiochipSpotID + 1;
+static const HotSpotID kBiochipDropSpotID = kInventoryDropSpotID + 1;
+
+static const HotSpotID kInfoReturnSpotID = kBiochipDropSpotID + 1;
+
+static const HotSpotID kAIHint1SpotID = kInfoReturnSpotID + 1;
+static const HotSpotID kAIHint2SpotID = kAIHint1SpotID + 1;
+static const HotSpotID kAIHint3SpotID = kAIHint2SpotID + 1;
+static const HotSpotID kAISolveSpotID = kAIHint3SpotID + 1;
+static const HotSpotID kAIBriefingSpotID = kAISolveSpotID + 1;
+static const HotSpotID kAIScanSpotID = kAIBriefingSpotID + 1;
+
+static const HotSpotID kPegasusRecallSpotID = kAIScanSpotID + 1;
+
+static const HotSpotID kAriesSpotID = kPegasusRecallSpotID + 1;
+static const HotSpotID kMercurySpotID = kAriesSpotID + 1;
+static const HotSpotID kPoseidonSpotID = kMercurySpotID + 1;
+
+static const HotSpotID kAirMaskToggleSpotID = kPoseidonSpotID + 1;
+
+static const HotSpotID kShuttleEnergySpotID = kAirMaskToggleSpotID + 1;
+static const HotSpotID kShuttleGravitonSpotID = kShuttleEnergySpotID + 1;
+static const HotSpotID kShuttleTractorSpotID = kShuttleGravitonSpotID + 1;
+static const HotSpotID kShuttleViewSpotID = kShuttleTractorSpotID + 1;
+static const HotSpotID kShuttleTransportSpotID = kShuttleViewSpotID + 1;
+
+// Most of these are obsolete:
+
+// kInventoryDropSpotFlag is a flag which marks hot spots which are valid drop spots
+// for inventory items.
+// static const HotSpotFlags kInventoryDropSpotFlag = kHighestGameShellSpotFlag << 1;
+
+// kBiochipDropSpotFlag is a flag which marks hot spots which are valid drop spots
+// for biochips.
+// static const HotSpotFlags kBiochipDropSpotFlag = kInventoryDropSpotFlag << 1;
+
+// kInventorySpotFlag is a flag which marks hot spots which indicate inventory items
+// in the environment.
+// static const HotSpotFlags kInventorySpotFlag = kBiochipDropSpotFlag << 1;
+
+// kBiochipSpotFlag is a flag which marks hot spots which indicate biochips
+// in the environment.
+static const HotSpotFlags kPickUpBiochipSpotFlag = kHighestGameShellSpotFlag << 1;
+static const HotSpotFlags kDropBiochipSpotFlag = kPickUpBiochipSpotFlag << 1;
+
+static const HotSpotFlags kInfoReturnSpotFlag = kDropBiochipSpotFlag << 1;
+
+// Biochip and inventory hot spot flags...
+
+static const HotSpotFlags kAIBiochipSpotFlag = kInfoReturnSpotFlag << 1;
+static const HotSpotFlags kPegasusBiochipSpotFlag = kAIBiochipSpotFlag << 1;
+static const HotSpotFlags kOpticalBiochipSpotFlag = kPegasusBiochipSpotFlag << 1;
+static const HotSpotFlags kAirMaskSpotFlag = kOpticalBiochipSpotFlag << 1;
+
+static const HotSpotFlags kJMPClickingSpotFlags = kClickSpotFlag |
+ kPlayExtraSpotFlag |
+ kOpenDoorSpotFlag |
+ kInfoReturnSpotFlag |
+ kAIBiochipSpotFlag |
+ kPegasusBiochipSpotFlag |
+ kOpticalBiochipSpotFlag |
+ kAirMaskSpotFlag;
+
+static const int32 kMainMenuID = 1;
+static const int32 kPauseMenuID = 2;
+static const int32 kCreditsMenuID = 3;
+static const int32 kDeathMenuID = 4;
+
+/////////////////////////////////////////////
+//
+// Menu commands.
+
+static const GameMenuCommand kMenuCmdOverview = kMenuCmdNoCommand + 1;
+static const GameMenuCommand kMenuCmdStartAdventure = kMenuCmdOverview + 1;
+static const GameMenuCommand kMenuCmdStartWalkthrough = kMenuCmdStartAdventure + 1;
+static const GameMenuCommand kMenuCmdRestore = kMenuCmdStartWalkthrough + 1;
+static const GameMenuCommand kMenuCmdCredits = kMenuCmdRestore + 1;
+static const GameMenuCommand kMenuCmdQuit = kMenuCmdCredits + 1;
+
+static const GameMenuCommand kMenuCmdDeathContinue = kMenuCmdQuit + 1;
+
+static const GameMenuCommand kMenuCmdDeathQuitDemo = kMenuCmdDeathContinue + 1;
+static const GameMenuCommand kMenuCmdDeathMainMenuDemo = kMenuCmdDeathQuitDemo + 1;
+
+static const GameMenuCommand kMenuCmdDeathRestore = kMenuCmdDeathMainMenuDemo + 1;
+static const GameMenuCommand kMenuCmdDeathMainMenu = kMenuCmdDeathRestore + 1;
+
+static const GameMenuCommand kMenuCmdPauseSave = kMenuCmdDeathMainMenu + 1;
+static const GameMenuCommand kMenuCmdPauseContinue = kMenuCmdPauseSave + 1;
+static const GameMenuCommand kMenuCmdPauseRestore = kMenuCmdPauseContinue + 1;
+static const GameMenuCommand kMenuCmdPauseQuit = kMenuCmdPauseRestore + 1;
+
+static const GameMenuCommand kMenuCmdCreditsMainMenu = kMenuCmdPauseQuit + 1;
+
+static const GameMenuCommand kMenuCmdCancelRestart = kMenuCmdCreditsMainMenu + 1;
+static const GameMenuCommand kMenuCmdEjectRestart = kMenuCmdCancelRestart + 1;
+
+static const TimeValue kMenuButtonHiliteTime = 20;
+static const TimeScale kMenuButtonHiliteScale = kSixtyTicksPerSecond;
+
+// PICT resources:
+
+// Warning light PICTs:
+
+static const ResIDType kLightOffID = 128;
+static const ResIDType kLightYellowID = 129;
+static const ResIDType kLightOrangeID = 130;
+static const ResIDType kLightRedID = 131;
+
+// Date PICTs:
+
+static const ResIDType kDatePrehistoricID = 138;
+static const ResIDType kDate2112ID = 139;
+static const ResIDType kDate2185ID = 140;
+static const ResIDType kDate2310ID = 141;
+static const ResIDType kDate2318ID = 142;
+
+/////////////////////////////////////////////
+//
+// Display Order
+
+static const DisplayOrder kCroppedMovieLayer = 11000;
+
+static const DisplayOrder kMonitorLayer = 12000;
+
+static const DisplayOrder kDragSpriteLayer = 15000;
+static const DisplayOrder kDragSpriteOrder = kDragSpriteLayer;
+
+static const DisplayOrder kInterfaceLayer = 20000;
+static const DisplayOrder kBackground1Order = kInterfaceLayer;
+static const DisplayOrder kBackground2Order = kBackground1Order + 1;
+static const DisplayOrder kBackground3Order = kBackground2Order + 1;
+static const DisplayOrder kBackground4Order = kBackground3Order + 1;
+static const DisplayOrder kDateOrder = kBackground4Order + 1;
+static const DisplayOrder kCompassOrder = kDateOrder + 1;
+static const DisplayOrder kEnergyBarOrder = kCompassOrder + 1;
+static const DisplayOrder kEnergyLightOrder = kEnergyBarOrder + 1;
+
+static const DisplayOrder kAILayer = 22000;
+static const DisplayOrder kAILeftAreaOrder = kAILayer;
+static const DisplayOrder kAIMiddleAreaOrder = kAILeftAreaOrder + 1;
+static const DisplayOrder kAIRightAreaOrder = kAIMiddleAreaOrder + 1;
+static const DisplayOrder kAIMovieOrder = kAIRightAreaOrder + 1;
+
+static const DisplayOrder kHilitesLayer = 23000;
+static const DisplayOrder kInventoryHiliteOrder = kHilitesLayer;
+static const DisplayOrder kBiochipHiliteOrder = kInventoryHiliteOrder + 1;
+
+static const DisplayOrder kPanelsLayer = 25000;
+static const DisplayOrder kInventoryPushOrder = kPanelsLayer;
+static const DisplayOrder kInventoryLidOrder = kInventoryPushOrder + 1;
+static const DisplayOrder kBiochipPushOrder = kInventoryLidOrder + 1;
+static const DisplayOrder kBiochipLidOrder = kBiochipPushOrder + 1;
+static const DisplayOrder kFinalMessageOrder = kBiochipLidOrder + 1;
+
+static const DisplayOrder kInfoLayer = 26000;
+static const DisplayOrder kInfoBackgroundOrder = kInfoLayer;
+static const DisplayOrder kInfoSpinOrder = kInfoBackgroundOrder + 1;
+
+static const DisplayOrder kScreenDimmerOrder = 30000;
+
+static const DisplayOrder kPauseScreenLayer = 31000;
+static const DisplayOrder kPauseMenuOrder = kPauseScreenLayer;
+static const DisplayOrder kSaveGameOrder = kPauseMenuOrder + 1;
+static const DisplayOrder kContinueOrder = kSaveGameOrder + 1;
+static const DisplayOrder kRestoreOrder = kContinueOrder + 1;
+static const DisplayOrder kSoundFXOrder = kRestoreOrder + 1;
+static const DisplayOrder kAmbienceOrder = kSoundFXOrder + 1;
+static const DisplayOrder kWalkthruOrder = kAmbienceOrder + 1;
+static const DisplayOrder kQuitToMainMenuOrder = kWalkthruOrder + 1;
+static const DisplayOrder kPauseLargeHiliteOrder = kQuitToMainMenuOrder + 1;
+static const DisplayOrder kPauseSmallHiliteOrder = kPauseLargeHiliteOrder + 1;
+
+/////////////////////////////////////////////
+//
+// Death reasons.
+enum {
+ // Caldoria
+ kDeathUncreatedInCaldoria = 1,
+ kDeathCardBomb,
+ kDeathShotBySinclair,
+ kDeathSinclairShotDelegate,
+ kDeathNuclearExplosion,
+
+ // TSA
+ kDeathUncreatedInTSA,
+ kDeathShotByTSARobots,
+
+ // Prehistoric
+ kDeathFallOffCliff,
+ kDeathEatenByDinosaur,
+ kDeathStranded,
+
+ // Norad
+ kDeathGassedInNorad,
+ kDeathArrestedInNorad,
+ kDeathWokeUpNorad,
+ kDeathSubDestroyed, // Unused
+ kDeathRobotThroughNoradDoor,
+ kDeathRobotSubControlRoom,
+
+ // Mars
+ kDeathWrongShuttleLock,
+ kDeathArrestedInMars,
+ kDeathRunOverByPod,
+ kDeathDidntGetOutOfWay,
+ kDeathReactorBurn,
+ kDeathDidntFindMarsBomb,
+ kDeathDidntDisarmMarsBomb,
+ kDeathNoMaskInMaze,
+ kDeathNoAirInMaze,
+ kDeathGroundByMazebot,
+ kDeathMissedOreBucket,
+ kDeathDidntLeaveBucket,
+ kDeathRanIntoCanyonWall, // Unused
+ kDeathRanIntoSpaceJunk,
+
+ // WSC
+ kDeathDidntStopPoison,
+ kDeathArrestedInWSC,
+ kDeathHitByPlasma,
+ kDeathShotOnCatwalk,
+
+ // Winning
+ kPlayerWonGame
+};
+
+static const CoordType kAILeftAreaLeft = 76;
+static const CoordType kAILeftAreaTop = 334;
+
+static const CoordType kAILeftAreaWidth = 96;
+static const CoordType kAILeftAreaHeight = 96;
+
+static const CoordType kAIMiddleAreaLeft = 172;
+static const CoordType kAIMiddleAreaTop = 334;
+
+static const CoordType kAIMiddleAreaWidth = 192;
+static const CoordType kAIMiddleAreaHeight = 96;
+
+static const CoordType kAIRightAreaLeft = 364;
+static const CoordType kAIRightAreaTop = 334;
+
+static const CoordType kAIRightAreaWidth = 96;
+static const CoordType kAIRightAreaHeight = 96;
+
+enum {
+ kTSAPlayerNotArrived, // initial state, must be zero
+ kTSAPlayerForcedReview, // Player must watch TBP before rip occurs.
+ kTSAPlayerDetectedRip, // Player finished TBP, rip alarm just went off.
+ kTSAPlayerNeedsHistoricalLog, // Player is instructed to get historical log
+ kTSAPlayerGotHistoricalLog,
+ kTSAPlayerInstalledHistoricalLog,
+ kTSABossSawHistoricalLog,
+ kRobotsAtCommandCenter,
+ kRobotsAtFrontDoor,
+ kRobotsAtReadyRoom,
+ kPlayerLockedInPegasus,
+ kPlayerOnWayToPrehistoric,
+ kPlayerWentToPrehistoric,
+ kPlayerOnWayToNorad,
+ kPlayerOnWayToMars,
+ kPlayerOnWayToWSC,
+ kPlayerFinishedWithTSA
+};
+
+/////////////////////////////////////////////
+//
+// Mode static constants.
+
+static const GameMode kModeInventoryPick = kLastGameShellMode + 1;
+static const GameMode kModeBiochipPick = kModeInventoryPick + 1;
+static const GameMode kModeInfoScreen = kModeBiochipPick + 1;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/cursor.cpp b/engines/pegasus/cursor.cpp
new file mode 100644
index 0000000000..5babdf34af
--- /dev/null
+++ b/engines/pegasus/cursor.cpp
@@ -0,0 +1,213 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/events.h"
+#include "common/stream.h"
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "graphics/surface.h"
+#include "graphics/decoders/pict.h"
+
+#include "pegasus/cursor.h"
+#include "pegasus/graphics.h"
+#include "pegasus/pegasus.h"
+
+namespace Pegasus {
+
+Cursor::Cursor() {
+ _cursorObscured = false;
+ _index = -1;
+ startIdling();
+}
+
+Cursor::~Cursor() {
+ for (uint32 i = 0; i < _info.size(); i++) {
+ if (_info[i].surface) {
+ _info[i].surface->free();
+ delete _info[i].surface;
+ }
+ delete[] _info[i].palette;
+ }
+
+ stopIdling();
+}
+
+void Cursor::addCursorFrames(uint16 id) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+ Common::SeekableReadStream *cursStream = vm->_resFork->getResource(MKTAG('C', 'u', 'r', 's'), id);
+ if (!cursStream)
+ error("Could not load cursor frames set %d", id);
+
+ uint16 frameCount = cursStream->readUint16BE();
+ for (uint16 i = 0; i < frameCount; i++) {
+ CursorInfo info;
+ info.tag = cursStream->readUint16BE();
+ info.hotspot.x = cursStream->readUint16BE();
+ info.hotspot.y = cursStream->readUint16BE();
+ info.surface = 0;
+ info.palette = 0;
+ info.colorCount = 0;
+ _info.push_back(info);
+ }
+
+ delete cursStream;
+
+ setCurrentFrameIndex(0);
+}
+
+void Cursor::setCurrentFrameIndex(int32 index) {
+ if (_index != index) {
+ _index = index;
+ if (index != -1) {
+ loadCursorImage(_info[index]);
+ CursorMan.replaceCursorPalette(_info[index].palette, 0, _info[index].colorCount);
+ CursorMan.replaceCursor((byte *)_info[index].surface->pixels, _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, 0);
+ ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty();
+ }
+ }
+}
+
+int32 Cursor::getCurrentFrameIndex() const {
+ return _index;
+}
+
+void Cursor::show() {
+ if (!isVisible())
+ CursorMan.showMouse(true);
+
+ _cursorObscured = false;
+ ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty();
+}
+
+void Cursor::hide() {
+ CursorMan.showMouse(false);
+ setCurrentFrameIndex(0);
+ ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty();
+}
+
+void Cursor::hideUntilMoved() {
+ if (!_cursorObscured) {
+ hide();
+ _cursorObscured = true;
+ }
+}
+
+void Cursor::useIdleTime() {
+ if (g_system->getEventManager()->getMousePos() != _cursorLocation) {
+ _cursorLocation = g_system->getEventManager()->getMousePos();
+ if (_index != -1 && _cursorObscured)
+ show();
+ ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty();
+ }
+}
+
+void Cursor::getCursorLocation(Common::Point &pt) const {
+ pt = _cursorLocation;
+}
+
+bool Cursor::isVisible() {
+ return CursorMan.isVisible();
+}
+
+void Cursor::loadCursorImage(CursorInfo &cursorInfo) {
+ if (cursorInfo.surface)
+ return;
+
+ cursorInfo.surface = new Graphics::Surface();
+
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+ Common::SeekableReadStream *cicnStream = vm->_resFork->getResource(MKTAG('c', 'i', 'c', 'n'), cursorInfo.tag);
+
+ if (!cicnStream)
+ error("Failed to find color icon %d", cursorInfo.tag);
+
+ // PixMap section
+ Graphics::PICTDecoder::PixMap pixMap = Graphics::PICTDecoder::readPixMap(*cicnStream);
+
+ // Mask section
+ cicnStream->readUint32BE(); // mask baseAddr
+ uint16 maskRowBytes = cicnStream->readUint16BE(); // mask rowBytes
+ cicnStream->skip(3 * 2); // mask rect
+ /* uint16 maskHeight = */ cicnStream->readUint16BE();
+
+ // Bitmap section
+ cicnStream->readUint32BE(); // baseAddr
+ uint16 rowBytes = cicnStream->readUint16BE();
+ cicnStream->readUint16BE(); // top
+ cicnStream->readUint16BE(); // left
+ uint16 height = cicnStream->readUint16BE(); // bottom
+ cicnStream->readUint16BE(); // right
+
+ // Data section
+ cicnStream->readUint32BE(); // icon handle
+ cicnStream->skip(maskRowBytes * height); // FIXME: maskHeight doesn't work here, though the specs say it should
+ cicnStream->skip(rowBytes * height);
+
+ // Palette section
+ cicnStream->readUint32BE(); // always 0
+ cicnStream->readUint16BE(); // always 0
+ cursorInfo.colorCount = cicnStream->readUint16BE() + 1;
+
+ cursorInfo.palette = new byte[cursorInfo.colorCount * 3];
+ for (uint16 i = 0; i < cursorInfo.colorCount; i++) {
+ cicnStream->readUint16BE();
+ cursorInfo.palette[i * 3] = cicnStream->readUint16BE() >> 8;
+ cursorInfo.palette[i * 3 + 1] = cicnStream->readUint16BE() >> 8;
+ cursorInfo.palette[i * 3 + 2] = cicnStream->readUint16BE() >> 8;
+ }
+
+ // PixMap data
+ if (pixMap.pixelSize == 8) {
+ cursorInfo.surface->create(pixMap.rowBytes, pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
+ cicnStream->read(cursorInfo.surface->pixels, pixMap.rowBytes * pixMap.bounds.height());
+
+ // While this looks sensible, it actually doesn't work for some cursors
+ // (ie. the 'can grab' hand)
+ //cursorInfo.surface->w = pixMap.bounds.width();
+ } else if (pixMap.pixelSize == 1) {
+ cursorInfo.surface->create(pixMap.bounds.width(), pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
+
+ for (int y = 0; y < pixMap.bounds.height(); y++) {
+ byte *line = (byte *)cursorInfo.surface->getBasePtr(0, y);
+
+ for (int x = 0; x < pixMap.bounds.width();) {
+ byte b = cicnStream->readByte();
+
+ for (int i = 0; i < 8; i++) {
+ *line++ = ((b & (1 << (7 - i))) != 0) ? 1 : 0;
+
+ if (++x == pixMap.bounds.width())
+ break;
+ }
+ }
+ }
+ } else {
+ error("Unhandled %dbpp cicn images", pixMap.pixelSize);
+ }
+
+ delete cicnStream;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/cursor.h b/engines/pegasus/cursor.h
new file mode 100644
index 0000000000..ada82e3967
--- /dev/null
+++ b/engines/pegasus/cursor.h
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_CURSOR_H
+#define PEGASUS_CURSOR_H
+
+#include "common/array.h"
+#include "common/rect.h"
+
+#include "pegasus/timers.h"
+
+namespace Graphics {
+ struct Surface;
+}
+
+namespace Pegasus {
+
+// The original cursor code was in the graphics code directly,
+// unlike ScummVM where we have the cursor code separate. We're
+// going to go with CursorManager here and therefore not inherit
+// from the Sprite class.
+
+class Cursor : private Idler {
+public:
+ Cursor();
+ virtual ~Cursor();
+
+ void addCursorFrames(uint16 id);
+
+ void setCurrentFrameIndex(int32 index);
+ int32 getCurrentFrameIndex() const;
+
+ void show();
+ void hide();
+ void hideUntilMoved();
+ bool isVisible();
+
+ void getCursorLocation(Common::Point &) const;
+
+protected:
+ virtual void useIdleTime();
+
+private:
+ struct CursorInfo {
+ uint16 tag;
+ Common::Point hotspot;
+ Graphics::Surface *surface;
+ byte *palette;
+ uint16 colorCount;
+ };
+
+ Common::Point _cursorLocation;
+ Common::Array<CursorInfo> _info;
+ bool _cursorObscured;
+ int _index;
+
+ void loadCursorImage(CursorInfo &cursorInfo);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp
new file mode 100644
index 0000000000..908005b665
--- /dev/null
+++ b/engines/pegasus/detection.cpp
@@ -0,0 +1,157 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/savefile.h"
+
+#include "pegasus/pegasus.h"
+
+namespace Pegasus {
+
+struct PegasusGameDescription {
+ ADGameDescription desc;
+};
+
+bool PegasusEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL)
+ || (f == kSupportsLoadingDuringRuntime)
+ || (f == kSupportsSavingDuringRuntime);
+}
+
+bool PegasusEngine::isDemo() const {
+ return (_gameDescription->desc.flags & ADGF_DEMO) != 0;
+}
+
+} // End of namespace Pegasus
+
+static const PlainGameDescriptor pegasusGames[] = {
+ {"pegasus", "The Journeyman Project: Pegasus Prime"},
+ {0, 0}
+};
+
+
+namespace Pegasus {
+
+static const PegasusGameDescription gameDescriptions[] = {
+ {
+ {
+ "pegasus",
+ "",
+ AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2009943),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO0()
+ },
+ },
+
+ {
+ {
+ "pegasus",
+ "Demo",
+ AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 360129),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK|ADGF_DEMO,
+ GUIO1(GUIO_NOLAUNCHLOAD)
+ },
+ },
+
+ { AD_TABLE_END_MARKER }
+};
+
+} // End of namespace Pegasus
+
+
+class PegasusMetaEngine : public AdvancedMetaEngine {
+public:
+ PegasusMetaEngine() : AdvancedMetaEngine(Pegasus::gameDescriptions, sizeof(Pegasus::PegasusGameDescription), pegasusGames) {
+ _singleid = "pegasus";
+ }
+
+ virtual const char *getName() const {
+ return "The Journeyman Project: Pegasus Prime";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "The Journeyman Project: Pegasus Prime (C) Presto Studios";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const { return 999; }
+ virtual void removeSaveState(const char *target, int slot) const;
+};
+
+bool PegasusMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves)
+ || (f == kSupportsLoadingDuringStartup)
+ || (f == kSupportsDeleteSave);
+}
+
+SaveStateList PegasusMetaEngine::listSaves(const char *target) const {
+ // The original had no pattern, so the user must rename theirs
+ // Note that we ignore the target because saves are compatible between
+ // all versions
+ Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav");
+
+ SaveStateList saveList;
+ for (uint32 i = 0; i < filenames.size(); i++) {
+ // Isolate the description from the file name
+ Common::String desc = filenames[i].c_str() + 8;
+ for (int j = 0; j < 4; j++)
+ desc.deleteLastChar();
+
+ saveList.push_back(SaveStateDescriptor(i, desc));
+ }
+
+ return saveList;
+}
+
+void PegasusMetaEngine::removeSaveState(const char *target, int slot) const {
+ // See listSaves() for info on the pattern
+ Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav");
+ g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str());
+}
+
+bool PegasusMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const Pegasus::PegasusGameDescription *gd = (const Pegasus::PegasusGameDescription *)desc;
+
+ if (gd)
+ *engine = new Pegasus::PegasusEngine(syst, gd);
+
+ return (gd != 0);
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(PEGASUS)
+ REGISTER_PLUGIN_DYNAMIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine);
+#endif
+
diff --git a/engines/pegasus/elements.cpp b/engines/pegasus/elements.cpp
new file mode 100644
index 0000000000..c84d555444
--- /dev/null
+++ b/engines/pegasus/elements.cpp
@@ -0,0 +1,568 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/macresman.h"
+#include "common/stream.h"
+
+#include "pegasus/elements.h"
+#include "pegasus/graphics.h"
+#include "pegasus/surface.h"
+
+namespace Pegasus {
+
+DisplayElement::DisplayElement(const DisplayElementID id) : IDObject(id) {
+ _elementIsDisplaying = false;
+ _elementIsVisible = false;
+ _elementOrder = 0;
+ _triggeredElement = this;
+ _nextElement = 0;
+}
+
+DisplayElement::~DisplayElement() {
+ if (isDisplaying())
+ ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
+}
+
+void DisplayElement::setDisplayOrder(const DisplayOrder order) {
+ if (_elementOrder != order) {
+ _elementOrder = order;
+ if (isDisplaying()) {
+ ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
+ ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this);
+ triggerRedraw();
+ }
+ }
+}
+
+void DisplayElement::startDisplaying() {
+ if (!isDisplaying()) {
+ ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this);
+ triggerRedraw();
+ }
+}
+
+void DisplayElement::stopDisplaying() {
+ if (isDisplaying()) {
+ triggerRedraw();
+ ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
+ }
+}
+
+void DisplayElement::setBounds(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) {
+ setBounds(Common::Rect(left, top, right, bottom));
+}
+
+void DisplayElement::getBounds(Common::Rect &r) const {
+ r = _bounds;
+}
+
+void DisplayElement::sizeElement(const CoordType h, const CoordType v) {
+ Common::Rect newBounds = _bounds;
+ newBounds.right = _bounds.left + h;
+ newBounds.bottom = _bounds.top + v;
+ setBounds(newBounds);
+}
+
+void DisplayElement::moveElementTo(const CoordType h, const CoordType v) {
+ Common::Rect newBounds = _bounds;
+ newBounds.moveTo(h, v);
+ setBounds(newBounds);
+}
+
+void DisplayElement::moveElement(const CoordType dh, const CoordType dv) {
+ Common::Rect newBounds = _bounds;
+ newBounds.translate(dh, dv);
+ setBounds(newBounds);
+}
+
+void DisplayElement::getLocation(CoordType &h, CoordType &v) const {
+ h = _bounds.left;
+ v = _bounds.top;
+}
+
+void DisplayElement::centerElementAt(const CoordType h, const CoordType v) {
+ Common::Rect newBounds = _bounds;
+ newBounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2));
+ setBounds(newBounds);
+}
+
+void DisplayElement::getCenter(CoordType &h, CoordType &v) const {
+ h = (_bounds.left + _bounds.right) / 2;
+ v = (_bounds.top + _bounds.bottom) / 2;
+}
+
+void DisplayElement::setBounds(const Common::Rect &r) {
+ if (r != _bounds) {
+ triggerRedraw();
+ _bounds = r;
+ triggerRedraw();
+ }
+}
+
+void DisplayElement::hide() {
+ if (_elementIsVisible) {
+ triggerRedraw();
+ _elementIsVisible = false;
+ }
+}
+
+void DisplayElement::show() {
+ if (!_elementIsVisible) {
+ _elementIsVisible = true;
+ triggerRedraw();
+ }
+}
+
+// Only invalidates this element's bounding rectangle if all these conditions are true:
+// -- The triggered element is this element.
+// -- The element is displaying on the display list.
+// -- The element is visible.
+// -- The element is part of the active layer OR is one of the reserved items.
+void DisplayElement::triggerRedraw() {
+ GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx;
+
+ if (_triggeredElement == this) {
+ if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer()))
+ gfx->invalRect(_bounds);
+ } else {
+ _triggeredElement->triggerRedraw();
+ }
+}
+
+void DisplayElement::setTriggeredElement(DisplayElement *element) {
+ if (element)
+ _triggeredElement = element;
+ else
+ _triggeredElement = this;
+}
+
+bool DisplayElement::validToDraw(DisplayOrder backLayer, DisplayOrder frontLayer) {
+ return isDisplaying() && _elementIsVisible &&
+ (getObjectID() <= kHighestReservedElementID ||
+ (getDisplayOrder() >= backLayer &&
+ getDisplayOrder() <= frontLayer));
+}
+
+DropHighlight::DropHighlight(const DisplayElementID id) : DisplayElement(id) {
+ _highlightColor = 0;
+ _thickness = 2;
+ _cornerDiameter = 0;
+}
+
+void DropHighlight::draw(const Common::Rect &) {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
+
+ // Since this is only used in two different ways, I'm only
+ // going to implement it in those two ways. Deal with it.
+
+ Common::Rect rect = _bounds;
+ rect.grow(-_thickness);
+ screen->frameRect(rect, _highlightColor);
+ rect.grow(1);
+ screen->frameRect(rect, _highlightColor);
+
+ if (_cornerDiameter == 8 && _thickness == 4) {
+ rect.grow(1);
+ screen->frameRect(rect, _highlightColor);
+ screen->hLine(rect.left + 1, rect.top - 1, rect.right - 2, _highlightColor);
+ screen->hLine(rect.left + 1, rect.bottom, rect.right - 2, _highlightColor);
+ screen->vLine(rect.left - 1, rect.top + 1, rect.bottom - 2, _highlightColor);
+ screen->vLine(rect.right, rect.top + 1, rect.bottom - 2, _highlightColor);
+ }
+}
+
+IdlerAnimation::IdlerAnimation(const DisplayElementID id) : Animation(id) {
+ _lastTime = 0xffffffff;
+}
+
+void IdlerAnimation::startDisplaying() {
+ if (!isDisplaying()) {
+ Animation::startDisplaying();
+ startIdling();
+ }
+}
+
+void IdlerAnimation::stopDisplaying() {
+ if (isDisplaying()) {
+ Animation::stopDisplaying();
+ stopIdling();
+ }
+}
+
+void IdlerAnimation::useIdleTime() {
+ uint32 currentTime = getTime();
+
+ if (currentTime != _lastTime) {
+ _lastTime = currentTime;
+ timeChanged(_lastTime);
+ }
+}
+
+void IdlerAnimation::timeChanged(const TimeValue) {
+ triggerRedraw();
+}
+
+FrameSequence::FrameSequence(const DisplayElementID id) : IdlerAnimation(id) {
+ _duration = 0;
+ _currentFrameNum = 0;
+ _resFork = new Common::MacResManager();
+ _numFrames = 0;
+}
+
+FrameSequence::~FrameSequence() {
+ delete _resFork;
+}
+
+void FrameSequence::useFileName(const Common::String &fileName) {
+ _resFork->open(fileName);
+}
+
+void FrameSequence::openFrameSequence() {
+ if (!_resFork->hasResFork())
+ return;
+
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80);
+
+ if (!res)
+ return;
+
+ uint32 scale = res->readUint32BE();
+ _bounds.top = res->readUint16BE();
+ _bounds.left = res->readUint16BE();
+ _bounds.bottom = res->readUint16BE();
+ _bounds.right = res->readUint16BE();
+ _numFrames = res->readUint16BE();
+ _duration = 0;
+
+ _frameTimes.clear();
+ for (uint32 i = 0; i < _numFrames; i++) {
+ TimeValue time = res->readUint32BE();
+ _duration += time;
+ _frameTimes.push_back(_duration);
+ }
+
+ setScale(scale);
+ setSegment(0, _duration);
+ setTime(0);
+ _currentFrameNum = 0;
+ newFrame(_currentFrameNum);
+ triggerRedraw();
+
+ delete res;
+}
+
+void FrameSequence::closeFrameSequence() {
+ stop();
+ _resFork->close();
+ _duration = 0;
+ _numFrames = 0;
+ _frameTimes.clear();
+}
+
+void FrameSequence::timeChanged(const TimeValue time) {
+ int16 frameNum = 0;
+ for (int16 i = _numFrames - 1; i >= 0; i--) {
+ if (_frameTimes[i] < time) {
+ frameNum = i;
+ break;
+ }
+ }
+
+ if (frameNum != _currentFrameNum) {
+ _currentFrameNum = frameNum;
+ newFrame(_currentFrameNum);
+ triggerRedraw();
+ }
+}
+
+void FrameSequence::setFrameNum(const int16 frameNum) {
+ int16 f = CLIP<int>(frameNum, 0, _numFrames);
+
+ if (_currentFrameNum != f) {
+ _currentFrameNum = f;
+ setTime(_frameTimes[f]);
+ newFrame(f);
+ triggerRedraw();
+ }
+}
+
+bool FrameSequence::isSequenceOpen() const {
+ return _numFrames != 0;
+}
+
+Sprite::Sprite(const DisplayElementID id) : DisplayElement(id) {
+ _numFrames = 0;
+ _currentFrameNum = 0xffffffff;
+ _currentFrame = 0;
+}
+
+Sprite::~Sprite() {
+ discardFrames();
+}
+
+void Sprite::discardFrames() {
+ if (!_frameArray.empty()) {
+ for (uint32 i = 0; i < _numFrames; i++) {
+ SpriteFrame *frame = _frameArray[i].frame;
+ frame->_referenceCount--;
+ if (frame->_referenceCount == 0)
+ delete frame;
+ }
+
+ _frameArray.clear();
+ _numFrames = 0;
+ _currentFrame = 0;
+ _currentFrameNum = 0xffffffff;
+ setBounds(0, 0, 0, 0);
+ }
+}
+
+void Sprite::addPICTResourceFrame(const ResIDType pictID, bool transparent, const CoordType left, const CoordType top) {
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, pictID, transparent);
+ addFrame(frame, left, top);
+}
+
+uint32 Sprite::addFrame(SpriteFrame *frame, const CoordType left, const CoordType top) {
+ SpriteFrameRec frameRecord;
+ frameRecord.frame = frame;
+ frameRecord.frameLeft = left;
+ frameRecord.frameTop = top;
+ _frameArray.push_back(frameRecord);
+ _numFrames++;
+ frame->_referenceCount++;
+
+ Common::Rect frameBounds;
+ frame->getSurfaceBounds(frameBounds);
+
+ // 9/3/96
+ // BB Should this be + left or - left?
+ frameBounds.moveTo(_bounds.left + left, _bounds.top + top);
+
+ frameBounds.extend(_bounds);
+
+ if (_bounds != frameBounds)
+ setBounds(frameBounds);
+
+ return _numFrames - 1;
+}
+
+void Sprite::removeFrame(const uint32 frameNum) {
+ _frameArray[frameNum].frame->_referenceCount--;
+ if (_frameArray[frameNum].frame->_referenceCount == 0)
+ delete _frameArray[frameNum].frame;
+
+ // Calculate the new bounds
+ Common::Rect frameBounds;
+ for (uint32 i = 0; i < _numFrames; i++) {
+ if (i == frameNum)
+ continue;
+
+ Common::Rect r;
+ _frameArray[i].frame->getSurfaceBounds(r);
+ r.translate(_frameArray[i].frameLeft, _frameArray[i].frameTop);
+ frameBounds.extend(r);
+ }
+
+ _frameArray.remove_at(frameNum);
+
+ frameBounds.moveTo(_bounds.left, _bounds.top);
+ setBounds(frameBounds);
+
+ if (_currentFrameNum == frameNum)
+ triggerRedraw();
+ else if (_currentFrameNum != 0xffffffff && _currentFrameNum > frameNum)
+ --_currentFrameNum;
+}
+
+void Sprite::setCurrentFrameIndex(const int32 frameNum) {
+ if (frameNum < 0) {
+ if (_currentFrameNum != 0xffffffff) {
+ _currentFrameNum = 0xffffffff;
+ _currentFrame = 0;
+ triggerRedraw();
+ }
+ } else if (_numFrames > 0) {
+ uint32 f = frameNum % _numFrames;
+ if (f != _currentFrameNum) {
+ _currentFrameNum = f;
+ _currentFrame = &_frameArray[f];
+ triggerRedraw();
+ }
+ }
+}
+
+SpriteFrame *Sprite::getFrame(const int32 index) {
+ if (index < 0 || (uint32)index >= _numFrames)
+ return 0;
+
+ return _frameArray[index].frame;
+}
+
+void Sprite::draw(const Common::Rect &r) {
+ if (_currentFrame) {
+ Common::Rect frameBounds;
+ _currentFrame->frame->getSurfaceBounds(frameBounds);
+
+ frameBounds.translate(_bounds.left + _currentFrame->frameLeft, _bounds.top + _currentFrame->frameTop);
+ Common::Rect r1 = frameBounds.findIntersectingRect(r);
+
+ Common::Rect r2 = frameBounds;
+ r2.translate(-_bounds.left - _currentFrame->frameLeft, -_bounds.top - _currentFrame->frameTop);
+
+ _currentFrame->frame->drawImage(r2, r1);
+ }
+}
+
+SpriteSequence::SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID) :
+ FrameSequence(id), _sprite(spriteID), _transparent(false) {
+}
+
+void SpriteSequence::openFrameSequence() {
+ if (!isSequenceOpen()) {
+ FrameSequence::openFrameSequence();
+
+ if (isSequenceOpen()) {
+ uint32 numFrames = getNumFrames();
+
+ for (uint32 i = 0; i < numFrames; ++i) {
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(_resFork, i + 0x80, _transparent);
+ _sprite.addFrame(frame, 0, 0);
+ }
+
+ _sprite.setBounds(_bounds);
+ }
+ }
+}
+
+void SpriteSequence::closeFrameSequence() {
+ if (isSequenceOpen()) {
+ FrameSequence::closeFrameSequence();
+ _sprite.discardFrames();
+ }
+}
+
+void SpriteSequence::setBounds(const Common::Rect &bounds) {
+ FrameSequence::setBounds(bounds);
+ _sprite.setBounds(_bounds);
+}
+
+void SpriteSequence::draw(const Common::Rect &r) {
+ _sprite.draw(r);
+}
+
+void SpriteSequence::newFrame(const uint16 frame) {
+ _sprite.setCurrentFrameIndex(frame);
+}
+
+#define DRAW_PIXEL() \
+ if (bytesPerPixel == 2) \
+ *((uint16 *)dst) = black; \
+ else \
+ *((uint32 *)dst) = black; \
+ dst += bytesPerPixel
+
+#define SKIP_PIXEL() \
+ dst += bytesPerPixel
+
+void ScreenDimmer::draw(const Common::Rect &r) {
+ // We're going to emulate QuickDraw's srcOr+gray mode here
+ // In this mode, every other y column is all black (odd-columns).
+ // Basically, every row does three black and then one transparent
+ // repeatedly.
+
+ // The output is identical to the original
+
+ uint32 black = g_system->getScreenFormat().RGBToColor(0, 0, 0);
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
+ byte bytesPerPixel = g_system->getScreenFormat().bytesPerPixel;
+
+ // We're currently doing it to the whole screen to simplify the code
+
+ for (int y = 0; y < 480; y++) {
+ byte *dst = (byte *)screen->getBasePtr(0, y);
+
+ for (int x = 0; x < 640; x += 4) {
+ if (y & 1) {
+ DRAW_PIXEL();
+ DRAW_PIXEL();
+ SKIP_PIXEL();
+ DRAW_PIXEL();
+ } else {
+ SKIP_PIXEL();
+ DRAW_PIXEL();
+ DRAW_PIXEL();
+ DRAW_PIXEL();
+ }
+ }
+ }
+}
+
+#undef DRAW_PIXEL
+#undef SKIP_PIXEL
+
+SoundLevel::SoundLevel(const DisplayElementID id) : DisplayElement(id) {
+ _soundLevel = 0;
+}
+
+void SoundLevel::incrementLevel() {
+ if (_soundLevel < 12) {
+ _soundLevel++;
+ triggerRedraw();
+ }
+}
+
+void SoundLevel::decrementLevel() {
+ if (_soundLevel > 0) {
+ _soundLevel--;
+ triggerRedraw();
+ }
+}
+
+uint16 SoundLevel::getSoundLevel() {
+ return CLIP<int>(_soundLevel * 22, 0, 256);
+}
+
+void SoundLevel::setSoundLevel(uint16 level) {
+ uint16 newLevel = (level + 21) / 22;
+
+ if (newLevel != _soundLevel) {
+ _soundLevel = newLevel;
+ triggerRedraw();
+ }
+}
+
+void SoundLevel::draw(const Common::Rect &r) {
+ Common::Rect levelRect(_bounds.right + (8 * (_soundLevel - 12)), _bounds.top, _bounds.right, _bounds.bottom);
+ levelRect = r.findIntersectingRect(levelRect);
+
+ if (!levelRect.isEmpty()) {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
+ screen->fillRect(levelRect, g_system->getScreenFormat().RGBToColor(0, 0, 0));
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/elements.h b/engines/pegasus/elements.h
new file mode 100644
index 0000000000..140553f675
--- /dev/null
+++ b/engines/pegasus/elements.h
@@ -0,0 +1,254 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ELEMENTS_H
+#define PEGASUS_ELEMENTS_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "common/system.h"
+#include "graphics/surface.h"
+
+#include "pegasus/timers.h"
+#include "pegasus/util.h"
+
+namespace Common {
+ class MacResManager;
+}
+
+namespace Pegasus {
+
+class DisplayElement : public IDObject {
+friend class GraphicsManager;
+public:
+ DisplayElement(const DisplayElementID);
+ virtual ~DisplayElement();
+
+ void setDisplayOrder(const DisplayOrder);
+ DisplayOrder getDisplayOrder() const { return _elementOrder; }
+
+ bool validToDraw(DisplayOrder, DisplayOrder);
+
+ virtual void draw(const Common::Rect&) {}
+ bool isDisplaying() { return _elementIsDisplaying; }
+ virtual void startDisplaying();
+ virtual void stopDisplaying();
+
+ virtual void show();
+ virtual void hide();
+ bool isVisible() { return _elementIsVisible; }
+
+ // triggerRedraw only triggers a draw if the element is displaying and visible.
+ void triggerRedraw();
+ void setTriggeredElement(DisplayElement *);
+
+ virtual void setBounds(const CoordType, const CoordType, const CoordType, const CoordType);
+ virtual void setBounds(const Common::Rect &);
+ virtual void getBounds(Common::Rect &) const;
+ virtual void sizeElement(const CoordType, const CoordType);
+ virtual void moveElementTo(const CoordType, const CoordType);
+ virtual void moveElement(const CoordType, const CoordType);
+ virtual void getLocation(CoordType &, CoordType &) const;
+ virtual void getCenter(CoordType &, CoordType &) const;
+ virtual void centerElementAt(const CoordType, const CoordType);
+
+protected:
+ Common::Rect _bounds;
+ bool _elementIsVisible;
+ DisplayElement *_triggeredElement;
+
+ // Used only by PegasusEngine
+ bool _elementIsDisplaying;
+ DisplayOrder _elementOrder;
+ DisplayElement *_nextElement;
+};
+
+// I'm using the proper "highlight" instead of the evil
+// QuickDraw "hilite" :P (deal with it!)
+class DropHighlight : public DisplayElement {
+public:
+ DropHighlight(const DisplayElementID);
+ virtual ~DropHighlight() {}
+
+ void setHighlightColor(const uint32 &highlight) { _highlightColor = highlight; }
+ void getHighlightColor(uint32 &highlight) const { highlight = _highlightColor; }
+
+ void setHighlightThickness(const uint16 thickness) { _thickness = thickness; }
+ uint16 getHighlightThickness() const { return _thickness; }
+
+ void setHighlightCornerDiameter(const uint16 diameter) { _cornerDiameter = diameter; }
+ uint16 getHighlightCornerDiameter() const { return _cornerDiameter; }
+
+ virtual void draw(const Common::Rect&);
+
+protected:
+ uint32 _highlightColor;
+ uint16 _thickness;
+ uint16 _cornerDiameter;
+};
+
+class Animation : public DisplayElement, public DynamicElement {
+public:
+ Animation(const DisplayElementID id) : DisplayElement(id) {}
+};
+
+class IdlerAnimation : public Animation, public Idler {
+public:
+ IdlerAnimation(const DisplayElementID);
+
+ virtual void startDisplaying();
+ virtual void stopDisplaying();
+
+ TimeValue getLastTime() const { return _lastTime; }
+
+protected:
+ virtual void useIdleTime();
+ virtual void timeChanged(const TimeValue);
+
+ TimeValue _lastTime;
+};
+
+// This class reads PICT resources and plays them like a movie.
+// Assumes there is a resource of type 'PFrm' describing the time values for each
+// PICT frame, as well as the total time in the movie.
+// Assumes that PICT frames begin at PICT 128
+
+class FrameSequence : public IdlerAnimation {
+public:
+ FrameSequence(const DisplayElementID);
+ virtual ~FrameSequence();
+
+ void useFileName(const Common::String &fileName);
+
+ virtual void openFrameSequence();
+ virtual void closeFrameSequence();
+ bool isSequenceOpen() const;
+
+ uint16 getNumFrames() const { return _numFrames; }
+ virtual uint16 getFrameNum() const { return _currentFrameNum; }
+ virtual void setFrameNum(const int16);
+
+protected:
+ virtual void timeChanged(const TimeValue);
+ virtual void newFrame(const uint16) {}
+
+ Common::MacResManager *_resFork;
+ TimeValue _duration;
+
+ uint16 _numFrames;
+ Common::Array<TimeValue> _frameTimes;
+
+ uint16 _currentFrameNum;
+};
+
+class SpriteFrame;
+
+class Sprite : public DisplayElement {
+friend class SpriteFrame;
+public:
+ Sprite(const DisplayElementID);
+ virtual ~Sprite();
+
+ virtual void addPICTResourceFrame(const ResIDType, const bool, const CoordType, const CoordType);
+ virtual uint32 addFrame(SpriteFrame *, const CoordType, const CoordType);
+ virtual void removeFrame(const uint32);
+ virtual void discardFrames();
+
+ // Setting the current frame.
+ // If the index is negative, sets the current frame to NULL and hides the sprite.
+ // If the index is larger than the number of frames in the sprite, the number
+ // is treated modulo the number of frames.
+ virtual void setCurrentFrameIndex(const int32);
+ virtual uint32 getCurrentFrameIndex() const { return _currentFrameNum; }
+
+ virtual SpriteFrame *getFrame(const int32);
+
+ virtual void draw(const Common::Rect &);
+
+ uint32 getNumFrames() const { return _numFrames; }
+
+protected:
+ struct SpriteFrameRec {
+ SpriteFrame *frame;
+ CoordType frameLeft;
+ CoordType frameTop;
+ };
+
+ uint32 _numFrames;
+ uint32 _currentFrameNum;
+ SpriteFrameRec *_currentFrame;
+ Common::Array<SpriteFrameRec> _frameArray;
+};
+
+class SpriteSequence : public FrameSequence {
+public:
+ SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID);
+ virtual ~SpriteSequence() {}
+
+ void useTransparent(bool transparent) { _transparent = transparent; }
+
+ virtual void openFrameSequence();
+ virtual void closeFrameSequence();
+
+ virtual void draw(const Common::Rect &);
+
+ virtual void setBounds(const Common::Rect &);
+
+protected:
+ virtual void newFrame(const uint16);
+
+ bool _transparent;
+ Sprite _sprite;
+};
+
+class ScreenDimmer : public DisplayElement {
+public:
+ ScreenDimmer() : DisplayElement(kScreenDimmerID) {}
+ virtual ~ScreenDimmer() {}
+
+ virtual void draw(const Common::Rect &);
+};
+
+class SoundLevel : public DisplayElement {
+public:
+ SoundLevel(const DisplayElementID);
+ virtual ~SoundLevel() {}
+
+ void incrementLevel();
+ void decrementLevel();
+
+ uint16 getSoundLevel();
+ void setSoundLevel(uint16);
+
+ void draw(const Common::Rect &);
+
+protected:
+ uint16 _soundLevel;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/energymonitor.cpp b/engines/pegasus/energymonitor.cpp
new file mode 100644
index 0000000000..8aa77eb341
--- /dev/null
+++ b/engines/pegasus/energymonitor.cpp
@@ -0,0 +1,296 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/surface.h"
+
+namespace Pegasus {
+
+Blinker::Blinker() {
+ _sprite = 0;
+ _frame1 = -1;
+ _frame2 = -1;
+ _blinkDuration = 0;
+}
+
+void Blinker::startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale) {
+ stopBlinking();
+ _sprite = sprite;
+ _frame1 = frame1;
+ _frame2 = frame2;
+ _blinkDuration = blinkDuration;
+ setScale(blinkScale);
+ setSegment(0, blinkDuration * numBlinks * 2, blinkScale);
+ setTime(0);
+ start();
+}
+
+void Blinker::stopBlinking() {
+ if (_sprite) {
+ _sprite->setCurrentFrameIndex(_frame2);
+ _sprite = 0;
+ stop();
+ }
+}
+
+void Blinker::timeChanged(const TimeValue time) {
+ if (_sprite && _blinkDuration != 0) {
+ if (((time / _blinkDuration) & 1) != 0 || time == getDuration()) {
+ _sprite->setCurrentFrameIndex(_frame2);
+ if (!isRunning())
+ stopBlinking();
+ } else {
+ _sprite->setCurrentFrameIndex(_frame1);
+ }
+ }
+}
+
+static const NotificationFlags kEnergyExpiredFlag = 1;
+
+EnergyMonitor *g_energyMonitor = 0;
+
+EnergyMonitor::EnergyMonitor() : IdlerAnimation(kEnergyBarID), _energyLight(kWarningLightID) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ _stage = kStageNoStage;
+
+ _calibrating = false;
+ _dontFlash = false;
+
+ setBounds(338, 48, 434, 54);
+
+ setDisplayOrder(kEnergyBarOrder);
+ startDisplaying();
+
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(vm->_resFork, kLightOffID);
+ _energyLight.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(vm->_resFork, kLightYellowID);
+ _energyLight.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(vm->_resFork, kLightOrangeID);
+ _energyLight.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(vm->_resFork, kLightRedID);
+ _energyLight.addFrame(frame, 0, 0);
+
+ _energyLight.setBounds(540, 35, 600, 59);
+ _energyLight.setDisplayOrder(kEnergyLightOrder);
+ _energyLight.startDisplaying();
+
+ setScale(1);
+ setSegment(0, kMaxJMPEnergy);
+
+ setEnergyValue(kCasualEnergy);
+
+ g_energyMonitor = this;
+}
+
+EnergyMonitor::~EnergyMonitor() {
+ g_energyMonitor = 0;
+}
+
+void EnergyMonitor::setEnergyValue(const uint32 value) {
+ if (isRunning()) {
+ stop();
+ setTime(getStop() - value);
+ start();
+ } else {
+ setTime(getStop() - value);
+ }
+}
+
+void EnergyMonitor::startEnergyDraining() {
+ if (!isRunning()) {
+ _energyLight.show();
+ start();
+ show();
+ }
+}
+
+void EnergyMonitor::setEnergyDrainRate(Common::Rational rate) {
+ setRate(rate);
+}
+
+Common::Rational EnergyMonitor::getEnergyDrainRate() {
+ return getRate();
+}
+
+void EnergyMonitor::stopEnergyDraining() {
+ if (isRunning()) {
+ stop();
+ _energyLight.hide();
+ hide();
+ }
+}
+
+void EnergyMonitor::drainEnergy(const int32 delta) {
+ setTime(getTime() + delta);
+}
+
+int32 EnergyMonitor::getCurrentEnergy() {
+ return kMaxJMPEnergy - getTime();
+}
+
+void EnergyMonitor::timeChanged(const TimeValue currentTime) {
+ if (currentTime == getStop()) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+ if (vm->getEnergyDeathReason() != -1)
+ vm->die(vm->getEnergyDeathReason());
+ } else {
+ uint32 currentEnergy = kMaxJMPEnergy - currentTime;
+
+ EnergyStage newStage;
+ if (currentEnergy > kWorriedEnergy)
+ newStage = kStageCasual;
+ else if (currentEnergy > kNervousEnergy)
+ newStage = kStageWorried;
+ else if (currentEnergy > kPanicStrickenEnergy)
+ newStage = kStageNervous;
+ else
+ newStage = kStagePanicStricken;
+
+ if (_stage != newStage) {
+ uint32 newFrame;
+
+ switch (newStage) {
+ case kStageCasual:
+ _barColor = g_system->getScreenFormat().RGBToColor(0x48, 0xB0, 0xD8);
+ newFrame = kFrameLightOff;
+ break;
+ case kStageWorried:
+ _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0xC0, 0x30);
+ newFrame = kFrameLightYellow;
+ break;
+ case kStageNervous:
+ _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x78, 0x38);
+ newFrame = kFrameLightOrange;
+ break;
+ case kStagePanicStricken:
+ _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x40, 0x38);
+ newFrame = kFrameLightRed;
+ break;
+ default:
+ error("no stage in energy monitor?");
+ break;
+ }
+
+ _stage = newStage;
+ uint32 oldFrame = _energyLight.getCurrentFrameIndex();
+
+ if (!_calibrating) {
+ if (oldFrame > newFrame || oldFrame == 0xffffffff || _dontFlash) {
+ _energyLight.setCurrentFrameIndex(newFrame);
+ _dontFlash = false;
+ } else {
+ _lightBlinker.startBlinking(&_energyLight, oldFrame, newFrame, 4, 1, 3);
+ triggerRedraw();
+ }
+ }
+ }
+
+ Common::Rect r;
+ calcLevelRect(r);
+ if (r != _levelRect) {
+ _levelRect = r;
+ triggerRedraw();
+ }
+ }
+}
+
+void EnergyMonitor::calcLevelRect(Common::Rect &r) {
+ if (getStop() == 0) {
+ r = Common::Rect();
+ } else {
+ getBounds(r);
+ r.left = r.right - r.width() * (kMaxJMPEnergy - getTime()) / getStop();
+ }
+}
+
+void EnergyMonitor::draw(const Common::Rect &r) {
+ Common::Rect r2 = r.findIntersectingRect(_levelRect);
+
+ if (!r2.isEmpty()) {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
+ screen->fillRect(r2, _barColor);
+ }
+}
+
+void EnergyMonitor::calibrateEnergyBar() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ _calibrating = true;
+
+ vm->setEnergyDeathReason(-1);
+
+ uint32 numFrames = _energyLight.getNumFrames();
+ for (uint32 i = 1; i < numFrames; i++) {
+ _energyLight.setCurrentFrameIndex(i);
+ _energyLight.show();
+ vm->delayShell(1, 3);
+ _energyLight.hide();
+ vm->delayShell(1, 3);
+ }
+
+ _energyLight.setCurrentFrameIndex(0);
+ _energyLight.hide();
+
+ show();
+ setEnergyValue(0);
+ setEnergyDrainRate(-(int32)kMaxJMPEnergy / 2);
+
+ // Make sure warning light is hidden...
+ _energyLight.hide();
+ while (getCurrentEnergy() != (int32)kMaxJMPEnergy) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ setEnergyDrainRate(0);
+ hide();
+
+ _calibrating = false;
+}
+
+void EnergyMonitor::restoreLastEnergyValue() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ _dontFlash = true;
+ setEnergyValue(vm->getSavedEnergyValue());
+ vm->resetEnergyDeathReason();
+}
+
+void EnergyMonitor::saveCurrentEnergyValue() {
+ ((PegasusEngine *)g_engine)->setLastEnergyValue(getCurrentEnergy());
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/energymonitor.h b/engines/pegasus/energymonitor.h
new file mode 100644
index 0000000000..02377d515a
--- /dev/null
+++ b/engines/pegasus/energymonitor.h
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ENERGYMONITOR_H
+#define PEGASUS_ENERGYMONITOR_H
+
+#include "pegasus/elements.h"
+
+namespace Pegasus {
+
+class Sprite;
+
+class Blinker : private IdlerTimeBase {
+public:
+ Blinker();
+ virtual ~Blinker() {}
+
+ void startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale);
+ void stopBlinking();
+
+protected:
+ virtual void timeChanged(const TimeValue);
+
+ Sprite *_sprite;
+ int32 _frame1;
+ int32 _frame2;
+ TimeValue _blinkDuration;
+};
+
+// Energy monitor constants.
+
+// These are in seconds.
+// Max is two hours
+static const uint32 kMaxJMPEnergy = 7200;
+
+static const uint32 kCasualEnergy = kMaxJMPEnergy * 100 / 100; // 100%
+static const uint32 kWorriedEnergy = kMaxJMPEnergy * 50 / 100; // 50%
+static const uint32 kNervousEnergy = kMaxJMPEnergy * 25 / 100; // 25%
+static const uint32 kPanicStrickenEnergy = kMaxJMPEnergy * 5 / 100; // 5%
+
+static const uint32 kFullEnergy = kCasualEnergy;
+
+static const uint32 kFrameLightOff = 0;
+static const uint32 kFrameLightYellow = 1;
+static const uint32 kFrameLightOrange = 2;
+static const uint32 kFrameLightRed = 3;
+
+static const int kEnergyDrainNormal = 1;
+static const int kMarsReactorEnergyDrainNoShield = 6;
+static const int kMarsReactorEnergyDrainWithShield = 3;
+static const int kWSCPoisonEnergyDrainWithDart = 20;
+static const int kWSCPoisonEnergyDrainNoDart = 10;
+
+class EnergyMonitor : private IdlerAnimation {
+public:
+ EnergyMonitor();
+ virtual ~EnergyMonitor();
+
+ void setEnergyValue(const uint32);
+ void startEnergyDraining();
+ void setEnergyDrainRate(Common::Rational);
+ Common::Rational getEnergyDrainRate();
+ void stopEnergyDraining();
+ void drainEnergy(const int32);
+ int32 getCurrentEnergy();
+
+ void restoreLastEnergyValue();
+ void saveCurrentEnergyValue();
+
+ void calibrateEnergyBar();
+
+protected:
+ void timeChanged(const TimeValue);
+ void calcLevelRect(Common::Rect &);
+ void draw(const Common::Rect &);
+
+ uint32 _barColor;
+ Common::Rect _levelRect;
+ EnergyStage _stage;
+ Sprite _energyLight;
+ Blinker _lightBlinker;
+ bool _calibrating, _dontFlash;
+};
+
+extern EnergyMonitor *g_energyMonitor;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/fader.cpp b/engines/pegasus/fader.cpp
new file mode 100644
index 0000000000..a2bbf22944
--- /dev/null
+++ b/engines/pegasus/fader.cpp
@@ -0,0 +1,218 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/fader.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/sound.h"
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+Fader::Fader() {
+ _currentValue = 0;
+ _currentFaderMove._numKnots = 0;
+}
+
+void Fader::setFaderValue(const int32 newValue) {
+ _currentValue = newValue;
+}
+
+bool Fader::initFaderMove(const FaderMoveSpec &spec) {
+ bool faderMoves = false;
+ int32 value = 0;
+
+ if (spec._numKnots > 0) {
+ stopFader();
+ value = spec._knots[0].knotValue;
+ TimeValue startTime = spec._knots[0].knotTime;
+
+ if (startTime != 0xffffffff) {
+ if (spec._numKnots > 1) {
+ TimeValue stopTime = spec._knots[spec._numKnots - 1].knotTime;
+
+ if (spec._faderScale > 0) {
+ if (stopTime > startTime) {
+ for (uint32 i = 1; i < spec._numKnots; ++i) {
+ if (spec._knots[i - 1].knotValue != spec._knots[i].knotValue) {
+ faderMoves = true;
+ break;
+ }
+ }
+
+ if (faderMoves)
+ _currentFaderMove = spec;
+ } else if (spec._knots[spec._numKnots - 1].knotValue != value) {
+ value = spec._knots[spec._numKnots - 1].knotValue;
+ }
+ }
+ }
+ }
+ }
+
+ setFaderValue(value);
+ return faderMoves;
+}
+
+void Fader::startFader(const FaderMoveSpec &spec) {
+ if (initFaderMove(spec)) {
+ setFlags(0);
+ setScale(spec._faderScale);
+ setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime);
+ setTime(spec._knots[0].knotTime);
+ start();
+ }
+}
+
+void Fader::startFaderSync(const FaderMoveSpec &spec) {
+ if (initFaderMove(spec)) {
+ setFlags(0);
+ setScale(spec._faderScale);
+ setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime);
+ setTime(spec._knots[0].knotTime);
+ start();
+
+ while (isFading()) {
+ ((PegasusEngine *)g_engine)->checkCallBacks();
+ useIdleTime();
+ }
+
+ // Once more, for good measure, to make sure that there are no boundary
+ // condition problems.
+ useIdleTime();
+ stopFader();
+ }
+}
+
+void Fader::loopFader(const FaderMoveSpec &spec) {
+ if (initFaderMove(spec)) {
+ setFlags(kLoopTimeBase);
+ setScale(spec._faderScale);
+ setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime);
+ setTime(spec._knots[0].knotTime);
+ start();
+ }
+}
+
+void Fader::stopFader() {
+ stop();
+}
+
+void Fader::pauseFader() {
+ stopFader();
+}
+
+void Fader::continueFader() {
+ if (getTime() < getStop())
+ start();
+}
+
+void Fader::timeChanged(const TimeValue newTime) {
+ if (_currentFaderMove._numKnots != 0) {
+ uint32 i;
+ for (i = 0; i < _currentFaderMove._numKnots; i++)
+ if (_currentFaderMove._knots[i].knotTime > newTime)
+ break;
+
+ int32 newValue;
+ if (i == 0)
+ newValue = _currentFaderMove._knots[0].knotValue;
+ else if (i == _currentFaderMove._numKnots)
+ newValue = _currentFaderMove._knots[i - 1].knotValue;
+ else
+ newValue = linearInterp(_currentFaderMove._knots[i - 1].knotTime, _currentFaderMove._knots[i].knotTime, newTime, _currentFaderMove._knots[i - 1].knotValue, _currentFaderMove._knots[i].knotValue);
+
+ if (newValue != _currentValue)
+ setFaderValue(newValue);
+ }
+}
+
+void FaderMoveSpec::makeOneKnotFaderSpec(const int32 knotValue) {
+ _numKnots = 1;
+ _knots[0].knotTime = 0;
+ _knots[0].knotValue = knotValue;
+}
+
+void FaderMoveSpec::makeTwoKnotFaderSpec(const TimeScale faderScale, const TimeValue time1, const int32 value1, const TimeValue time2, const int32 value2) {
+ _numKnots = 2;
+ _faderScale = faderScale;
+ _knots[0].knotTime = time1;
+ _knots[0].knotValue = value1;
+ _knots[1].knotTime = time2;
+ _knots[1].knotValue = value2;
+}
+
+void FaderMoveSpec::insertFaderKnot(const TimeValue knotTime, const int32 knotValue) {
+ if (_numKnots != kMaxFaderKnots) {
+ uint32 index;
+ for (index = 0; index < _numKnots; index++) {
+ if (knotTime == _knots[index].knotTime) {
+ _knots[index].knotValue = knotValue;
+ return;
+ } else if (knotTime < _knots[index].knotTime) {
+ break;
+ }
+ }
+
+ for (uint32 i = _numKnots; i > index; i--)
+ _knots[i] = _knots[i - 1];
+
+ _knots[index].knotTime = knotTime;
+ _knots[index].knotValue = knotValue;
+ _numKnots++;
+ }
+}
+
+void FaderAnimation::setFaderValue(const int32 newValue) {
+ if (getFaderValue() != newValue) {
+ Fader::setFaderValue(newValue);
+ triggerRedraw();
+ }
+}
+
+SoundFader::SoundFader() {
+ _sound = 0;
+ _masterVolume = 0xff;
+}
+
+void SoundFader::attachSound(Sound *sound) {
+ if (!sound && isFading())
+ stopFader();
+
+ _sound = sound;
+}
+
+void SoundFader::setFaderValue(const int32 newVolume) {
+ if (_sound)
+ _sound->setVolume((newVolume * _masterVolume) >> 8);
+
+ _currentValue = newVolume;
+}
+
+void SoundFader::setMasterVolume(const uint16 masterVolume) {
+ _masterVolume = masterVolume;
+ setFaderValue(getFaderValue());
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/fader.h b/engines/pegasus/fader.h
new file mode 100644
index 0000000000..0a8cd549e6
--- /dev/null
+++ b/engines/pegasus/fader.h
@@ -0,0 +1,130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_FADER_H
+#define PEGASUS_FADER_H
+
+#include "pegasus/elements.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class Fader;
+
+class FaderMoveSpec {
+friend class Fader;
+public:
+ FaderMoveSpec() {
+ _faderScale = kDefaultTimeScale;
+ _numKnots = 0;
+ }
+
+ FaderMoveSpec(const TimeScale scale) {
+ _faderScale = scale;
+ _numKnots = 0;
+ }
+
+ void setFaderScale(const TimeScale scale) { _faderScale = scale; }
+ TimeScale getFaderScale() const { return _faderScale; }
+
+ void makeOneKnotFaderSpec(const int32);
+ void makeTwoKnotFaderSpec(const TimeScale, const TimeValue, const int32, const TimeValue, const int32);
+
+ void insertFaderKnot(const TimeValue, const int32);
+
+ uint32 getNumKnots() const { return _numKnots; }
+ TimeValue getNthKnotTime(const uint32 index) const { return _knots[index].knotTime; }
+ int32 getNthKnotValue(const uint32 index) const { return _knots[index].knotValue; }
+
+protected:
+ struct FaderKnot {
+ TimeValue knotTime;
+ int32 knotValue;
+ };
+
+ TimeScale _faderScale;
+ uint32 _numKnots;
+
+ static const uint32 kMaxFaderKnots = 20;
+ FaderKnot _knots[kMaxFaderKnots];
+};
+
+class Fader : public IdlerTimeBase {
+public:
+ Fader();
+ virtual ~Fader() {}
+
+ virtual void setFaderValue(const int32);
+ int32 getFaderValue() const { return _currentValue; }
+ virtual void startFader(const FaderMoveSpec &);
+ virtual void startFaderSync(const FaderMoveSpec &);
+ virtual void loopFader(const FaderMoveSpec &);
+ virtual void stopFader();
+ virtual bool isFading() { return isRunning(); }
+
+ void pauseFader();
+ void continueFader();
+
+ void getCurrentFaderMove(FaderMoveSpec &spec) { spec = _currentFaderMove; }
+
+protected:
+ bool initFaderMove(const FaderMoveSpec &);
+ virtual void timeChanged(const TimeValue);
+
+ int32 _currentValue;
+ FaderMoveSpec _currentFaderMove;
+};
+
+class FaderAnimation : public DisplayElement, public Fader {
+public:
+ FaderAnimation(const DisplayElementID id) : DisplayElement(id) {}
+ virtual ~FaderAnimation() {}
+
+ void setFaderValue(const int32);
+};
+
+class Sound;
+
+class SoundFader : public Fader {
+friend class Sound;
+public:
+ SoundFader();
+ virtual ~SoundFader() {}
+
+ void setFaderValue(const int32);
+
+ void setMasterVolume(const uint16);
+ uint16 getMasterVolume() const { return _masterVolume; }
+
+protected:
+ void attachSound(Sound *);
+
+ Sound *_sound;
+ uint16 _masterVolume;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/gamestate.cpp b/engines/pegasus/gamestate.cpp
new file mode 100644
index 0000000000..7a4e657e02
--- /dev/null
+++ b/engines/pegasus/gamestate.cpp
@@ -0,0 +1,2359 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/error.h"
+#include "common/stream.h"
+
+#include "pegasus/constants.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/scoring.h"
+
+namespace Common {
+DECLARE_SINGLETON(Pegasus::GameStateManager);
+}
+
+namespace Pegasus {
+
+Common::Error GameStateManager::writeGameState(Common::WriteStream *stream) {
+ stream->writeUint16BE(_currentNeighborhood);
+ stream->writeUint16BE(_currentRoom);
+ stream->writeByte(_currentDirection);
+ stream->writeUint16BE(_nexNeighborhoodID);
+ stream->writeUint16BE(_nextRoomID);
+ stream->writeByte(_nextDirection);
+ stream->writeUint16BE(_lastNeighborhood);
+ stream->writeUint16BE(_lastRoom);
+ stream->writeByte(_lastDirection);
+ stream->writeUint16BE(_openDoorRoom);
+ stream->writeByte(_openDoorDirection);
+
+ _globalFlags.writeToStream(stream);
+ _scoringFlags.writeToStream(stream);
+ _itemTakenFlags.writeToStream(stream);
+
+ writeCaldoriaState(stream);
+ writeTSAState(stream);
+ writePrehistoricState(stream);
+ writeNoradState(stream);
+ writeMarsState(stream);
+ writeWSCState(stream);
+
+ if (stream->err())
+ return Common::kWritingFailed;
+
+ return Common::kNoError;
+}
+
+Common::Error GameStateManager::readGameState(Common::ReadStream *stream) {
+ _currentNeighborhood = stream->readUint16BE();
+ _currentRoom = stream->readUint16BE();
+ _currentDirection = stream->readByte();
+ _nexNeighborhoodID = stream->readUint16BE();
+ _nextRoomID = stream->readUint16BE();
+ _nextDirection = stream->readByte();
+ _lastNeighborhood = stream->readUint16BE();
+ _lastRoom = stream->readUint16BE();
+ _lastDirection = stream->readByte();
+ _openDoorRoom = stream->readUint16BE();
+ _openDoorDirection = stream->readByte();
+
+ _globalFlags.readFromStream(stream);
+ _scoringFlags.readFromStream(stream);
+ _itemTakenFlags.readFromStream(stream);
+
+ readCaldoriaState(stream);
+ readTSAState(stream);
+ readPrehistoricState(stream);
+ readNoradState(stream);
+ readMarsState(stream);
+ readWSCState(stream);
+
+ if (stream->err())
+ return Common::kReadingFailed;
+
+ return Common::kNoError;
+}
+
+void GameStateManager::resetGameState() {
+ _currentNeighborhood = kNoNeighborhoodID;
+ _currentRoom = kNoRoomID;
+ _currentDirection = kNoDirection;
+ _nexNeighborhoodID = kNoNeighborhoodID;
+ _nextRoomID = kNoRoomID;
+ _nextDirection = kNoDirection;
+ _lastNeighborhood = kNoNeighborhoodID;
+ _lastRoom = kNoRoomID;
+ _lastDirection = kNoDirection;
+ _openDoorRoom = kNoRoomID;
+ _openDoorDirection = kNoDirection;
+
+ _globalFlags.clearAllFlags();
+ _scoringFlags.clearAllFlags();
+ _itemTakenFlags.clearAllFlags();
+
+ resetCaldoriaState();
+ resetTSAState();
+ resetPrehistoricState();
+ resetNoradState();
+ resetMarsState();
+ resetWSCState();
+}
+
+void GameStateManager::getCurrentLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) {
+ neighborhood = _currentNeighborhood;
+ room = _currentRoom;
+ direction = _currentDirection;
+}
+
+void GameStateManager::setCurrentLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) {
+ _lastNeighborhood = _currentNeighborhood;
+ _lastRoom = _currentRoom;
+ _lastDirection = _currentDirection;
+ _currentNeighborhood = neighborhood;
+ _currentRoom = room;
+ _currentDirection = direction;
+}
+
+NeighborhoodID GameStateManager::getCurrentNeighborhood() {
+ return _currentNeighborhood;
+}
+
+void GameStateManager::setCurrentNeighborhood(const NeighborhoodID neighborhood) {
+ _lastNeighborhood = _currentNeighborhood;
+ _currentNeighborhood = neighborhood;
+}
+
+RoomID GameStateManager::getCurrentRoom() {
+ return _currentRoom;
+}
+
+void GameStateManager::setCurrentRoom(const RoomID room) {
+ _lastRoom = _currentRoom;
+ _currentRoom = room;
+}
+
+DirectionConstant GameStateManager::getCurrentDirection() {
+ return _currentDirection;
+}
+
+void GameStateManager::setCurrentDirection(const DirectionConstant direction) {
+ _lastDirection = _currentDirection;
+ _currentDirection = direction;
+}
+
+RoomViewID GameStateManager::getCurrentRoomAndView() {
+ return MakeRoomView(_currentRoom, _currentDirection);
+}
+
+void GameStateManager::getNextLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) {
+ neighborhood = _nexNeighborhoodID;
+ room = _nextRoomID;
+ direction = _nextDirection;
+}
+
+void GameStateManager::setNextLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) {
+ _nexNeighborhoodID = neighborhood;
+ _nextRoomID = room;
+ _nextDirection = direction;
+}
+
+NeighborhoodID GameStateManager::getNextNeighborhood() {
+ return _nexNeighborhoodID;
+}
+
+void GameStateManager::setNextNeighborhood(const NeighborhoodID neighborhood) {
+ _nexNeighborhoodID = neighborhood;
+}
+
+RoomID GameStateManager::getNextRoom() {
+ return _nextRoomID;
+}
+
+void GameStateManager::setNextRoom(const RoomID room) {
+ _nextRoomID = room;
+}
+
+DirectionConstant GameStateManager::getNextDirection() {
+ return _nextDirection;
+}
+
+void GameStateManager::setNextDirection(const DirectionConstant direction) {
+ _nextDirection = direction;
+}
+
+void GameStateManager::getLastLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) {
+ neighborhood = _currentNeighborhood;
+ room = _currentRoom;
+ direction = _currentDirection;
+}
+
+void GameStateManager::setLastLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) {
+ _currentNeighborhood = neighborhood;
+ _currentRoom = room;
+ _currentDirection = direction;
+}
+
+NeighborhoodID GameStateManager::getLastNeighborhood() {
+ return _lastNeighborhood;
+}
+
+void GameStateManager::setLastNeighborhood(const NeighborhoodID neighborhood) {
+ _lastNeighborhood = neighborhood;
+}
+
+RoomID GameStateManager::getLastRoom() {
+ return _lastRoom;
+}
+
+void GameStateManager::setLastRoom(const RoomID room) {
+ _lastRoom = room;
+}
+
+DirectionConstant GameStateManager::getLastDirection() {
+ return _lastDirection;
+}
+
+void GameStateManager::setLastDirection(const DirectionConstant direction) {
+ _lastDirection = direction;
+}
+
+RoomViewID GameStateManager::getLastRoomAndView() {
+ return MakeRoomView(_lastRoom, _lastDirection);
+}
+
+void GameStateManager::getOpenDoorLocation(RoomID &room, DirectionConstant &direction) {
+ room = _openDoorRoom;
+ direction = _openDoorDirection;
+}
+
+void GameStateManager::setOpenDoorLocation(const RoomID room, const DirectionConstant direction) {
+ _openDoorRoom = room;
+ _openDoorDirection = direction;
+}
+
+RoomID GameStateManager::getOpenDoorRoom() {
+ return _openDoorRoom;
+}
+
+void GameStateManager::setOpenDoorRoom(const RoomID room) {
+ _openDoorRoom = room;
+}
+
+DirectionConstant GameStateManager::getOpenDoorDirection() {
+ return _openDoorDirection;
+}
+
+void GameStateManager::setOpenDoorDirection(const DirectionConstant direction) {
+ _openDoorDirection = direction;
+}
+
+RoomViewID GameStateManager::getDoorOpenRoomAndView() {
+ return MakeRoomView(_openDoorRoom, _openDoorDirection);
+}
+
+bool GameStateManager::isCurrentDoorOpen() {
+ return _openDoorRoom == _currentRoom && _openDoorDirection == _currentDirection;
+}
+
+GameScoreType GameStateManager::getCaldoriaTSAScore() {
+ GameScoreType result = 0;
+
+ if (_scoringFlags.getFlag(kScoringSawINNFlag))
+ result += kSawINNScore;
+ if (_scoringFlags.getFlag(kScoringTookShowerFlag))
+ result += kTookShowerScore;
+ if (_scoringFlags.getFlag(kScoringFixedHairFlag))
+ result += kFixedHairScore;
+ if (_scoringFlags.getFlag(kScoringGotKeyCardFlag))
+ result += kGotKeyCardScore;
+ if (_scoringFlags.getFlag(kScoringReadPaperFlag))
+ result += kReadPaperScore;
+ if (_scoringFlags.getFlag(kScoringLookThroughTelescopeFlag))
+ result += kLookThroughTelescopeScore;
+ if (_scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag))
+ result += kSawCaldoriaKioskScore;
+ if (_scoringFlags.getFlag(kScoringGoToTSAFlag))
+ result += kGoToTSAScore;
+ if (_scoringFlags.getFlag(kScoringEnterTSAFlag))
+ result += kEnterTSAScore;
+ if (_scoringFlags.getFlag(kScoringSawBust1Flag))
+ result += kSawBust1Score;
+ if (_scoringFlags.getFlag(kScoringSawBust2Flag))
+ result += kSawBust2Score;
+ if (_scoringFlags.getFlag(kScoringSawBust3Flag))
+ result += kSawBust3Score;
+ if (_scoringFlags.getFlag(kScoringSawBust4Flag))
+ result += kSawBust4Score;
+ if (_scoringFlags.getFlag(kScoringSawBust5Flag))
+ result += kSawBust5Score;
+ if (_scoringFlags.getFlag(kScoringSawBust6Flag))
+ result += kSawBust6Score;
+ if (_scoringFlags.getFlag(kScoringSawTheoryFlag))
+ result += kSawTheoryScore;
+ if (_scoringFlags.getFlag(kScoringSawBackgroundFlag))
+ result += kSawBackgroundScore;
+ if (_scoringFlags.getFlag(kScoringSawProcedureFlag))
+ result += kSawProcedureScore;
+ if (_scoringFlags.getFlag(kScoringGotJourneymanKeyFlag))
+ result += kGotJourneymanKeyScore;
+ if (_scoringFlags.getFlag(kScoringGotPegasusBiochipFlag))
+ result += kGotPegasusBiochipScore;
+ if (_scoringFlags.getFlag(kScoringGotBiosuitFlag))
+ result += kGotBiosuitScore;
+ if (_scoringFlags.getFlag(kScoringGoToPrehistoricFlag))
+ result += kGoToPrehistoricScore;
+ if (_scoringFlags.getFlag(kScoringPutLogInReaderFlag))
+ result += kPutLogInReaderScore;
+ if (_scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag))
+ result += kSawCaldoriaNormalScore;
+ if (_scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag))
+ result += kSawCaldoriaAlteredScore;
+ if (_scoringFlags.getFlag(kScoringSawNoradNormalFlag))
+ result += kSawNoradNormalScore;
+ if (_scoringFlags.getFlag(kScoringSawNoradAlteredFlag))
+ result += kSawNoradAlteredScore;
+ if (_scoringFlags.getFlag(kScoringSawMarsNormalFlag))
+ result += kSawMarsNormalScore;
+ if (_scoringFlags.getFlag(kScoringSawMarsAlteredFlag))
+ result += kSawMarsAlteredScore;
+ if (_scoringFlags.getFlag(kScoringSawWSCNormalFlag))
+ result += kSawWSCNormalScore;
+ if (_scoringFlags.getFlag(kScoringSawWSCAlteredFlag))
+ result += kSawWSCAlteredScore;
+ if (_scoringFlags.getFlag(kScoringWentToReadyRoom2Flag))
+ result += kWentToReadyRoom2Score;
+ if (_scoringFlags.getFlag(kScoringWentAfterSinclairFlag))
+ result += kWentAfterSinclairScore;
+ if (_scoringFlags.getFlag(kScoringUsedCardBombFlag))
+ result += kUsedCardBombScore;
+ if (_scoringFlags.getFlag(kScoringShieldedCardBombFlag))
+ result += kShieldedCardBombScore;
+ if (_scoringFlags.getFlag(kScoringStunnedSinclairFlag))
+ result += kStunnedSinclairScore;
+ if (_scoringFlags.getFlag(kScoringDisarmedNukeFlag))
+ result += kDisarmedNukeScore;
+
+ return result;
+}
+
+GameScoreType GameStateManager::getPrehistoricScore() {
+ GameScoreType result = 0;
+
+ if (_scoringFlags.getFlag(kScoringThrewBreakerFlag))
+ result += kThrewBreakerScore;
+ if (_scoringFlags.getFlag(kScoringExtendedBridgeFlag))
+ result += kExtendedBridgeScore;
+ if (_scoringFlags.getFlag(kScoringGotHistoricalLogFlag))
+ result += kGotHistoricalLogScore;
+ if (_scoringFlags.getFlag(kScoringFinishedPrehistoricFlag))
+ result += kFinishedPrehistoricScore;
+
+ return result;
+}
+
+GameScoreType GameStateManager::getMarsScore() {
+ GameScoreType result = 0;
+
+ if (_scoringFlags.getFlag(kScoringThrownByRobotFlag))
+ result += kThrownByRobotScore;
+ if (_scoringFlags.getFlag(kScoringGotMarsCardFlag))
+ result += kGotMarsCardScore;
+ if (_scoringFlags.getFlag(kScoringSawMarsKioskFlag))
+ result += kSawMarsKioskScore;
+ if (_scoringFlags.getFlag(kScoringSawTransportMapFlag))
+ result += kSawTransportMapScore;
+ if (_scoringFlags.getFlag(kScoringGotCrowBarFlag))
+ result += kGotCrowBarScore;
+ if (_scoringFlags.getFlag(kScoringTurnedOnTransportFlag))
+ result += kTurnedOnTransportScore;
+ if (_scoringFlags.getFlag(kScoringGotOxygenMaskFlag))
+ result += kGotOxygenMaskScore;
+ if (_scoringFlags.getFlag(kScoringAvoidedRobotFlag))
+ result += kAvoidedRobotScore;
+ if (_scoringFlags.getFlag(kScoringActivatedPlatformFlag))
+ result += kActivatedPlatformScore;
+ if (_scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag))
+ result += kUsedLiquidNitrogenScore;
+ if (_scoringFlags.getFlag(kScoringUsedCrowBarFlag))
+ result += kUsedCrowBarScore;
+ if (_scoringFlags.getFlag(kScoringFoundCardBombFlag))
+ result += kFoundCardBombScore;
+ if (_scoringFlags.getFlag(kScoringDisarmedCardBombFlag))
+ result += kDisarmedCardBombScore;
+ if (_scoringFlags.getFlag(kScoringGotCardBombFlag))
+ result += kGotCardBombScore;
+ if (_scoringFlags.getFlag(kScoringThreadedMazeFlag))
+ result += kThreadedMazeScore;
+ if (_scoringFlags.getFlag(kScoringThreadedGearRoomFlag))
+ result += kThreadedGearRoomScore;
+ if (_scoringFlags.getFlag(kScoringEnteredShuttleFlag))
+ result += kEnteredShuttleScore;
+ if (_scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag))
+ result += kEnteredLaunchTubeScore;
+ if (_scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag))
+ result += kStoppedRobotsShuttleScore;
+ if (_scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag))
+ result += kGotMarsOpMemChipScore;
+ if (_scoringFlags.getFlag(kScoringFinishedMarsFlag))
+ result += kFinishedMarsScore;
+
+ return result;
+}
+
+GameScoreType GameStateManager::getNoradScore() {
+ GameScoreType result = 0;
+
+ if (_scoringFlags.getFlag(kScoringSawSecurityMonitorFlag))
+ result += kSawSecurityMonitorScore;
+ if (_scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag))
+ result += kFilledOxygenCanisterScore;
+ if (_scoringFlags.getFlag(kScoringFilledArgonCanisterFlag))
+ result += kFilledArgonCanisterScore;
+ if (_scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag))
+ result += kSawUnconsciousOperatorScore;
+ if (_scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag))
+ result += kWentThroughPressureDoorScore;
+ if (_scoringFlags.getFlag(kScoringPreppedSubFlag))
+ result += kPreppedSubScore;
+ if (_scoringFlags.getFlag(kScoringEnteredSubFlag))
+ result += kEnteredSubScore;
+ if (_scoringFlags.getFlag(kScoringExitedSubFlag))
+ result += kExitedSubScore;
+ if (_scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag))
+ result += kSawRobotAt54NorthScore;
+ if (_scoringFlags.getFlag(kScoringPlayedWithClawFlag))
+ result += kPlayedWithClawScore;
+ if (_scoringFlags.getFlag(kScoringUsedRetinalChipFlag))
+ result += kUsedRetinalChipScore;
+ if (_scoringFlags.getFlag(kScoringFinishedGlobeGameFlag))
+ result += kFinishedGlobeGameScore;
+ if (_scoringFlags.getFlag(kScoringStoppedNoradRobotFlag))
+ result += kStoppedNoradRobotScore;
+ if (_scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag))
+ result += kGotNoradOpMemChipScore;
+ if (_scoringFlags.getFlag(kScoringFinishedNoradFlag))
+ result += kFinishedNoradScore;
+
+ return result;
+}
+
+GameScoreType GameStateManager::getWSCScore() {
+ GameScoreType result = 0;
+
+ if (_scoringFlags.getFlag(kScoringRemovedDartFlag))
+ result += kRemovedDartScore;
+ if (_scoringFlags.getFlag(kScoringAnalyzedDartFlag))
+ result += kAnalyzedDartScore;
+ if (_scoringFlags.getFlag(kScoringBuiltAntidoteFlag))
+ result += kBuiltAntidoteScore;
+ if (_scoringFlags.getFlag(kScoringGotSinclairKeyFlag))
+ result += kGotSinclairKeyScore;
+ if (_scoringFlags.getFlag(kScoringGotArgonCanisterFlag))
+ result += kGotArgonCanisterScore;
+ if (_scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag))
+ result += kGotNitrogenCanisterScore;
+ if (_scoringFlags.getFlag(kScoringPlayedWithMessagesFlag))
+ result += kPlayedWithMessagesScore;
+ if (_scoringFlags.getFlag(kScoringSawMorphExperimentFlag))
+ result += kSawMorphExperimentScore;
+ if (_scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag))
+ result += kEnteredSinclairOfficeScore;
+ if (_scoringFlags.getFlag(kScoringSawBrochureFlag))
+ result += kSawBrochureScore;
+ if (_scoringFlags.getFlag(kScoringSawSinclairEntry1Flag))
+ result += kSawSinclairEntry1Score;
+ if (_scoringFlags.getFlag(kScoringSawSinclairEntry2Flag))
+ result += kSawSinclairEntry2Score;
+ if (_scoringFlags.getFlag(kScoringSawSinclairEntry3Flag))
+ result += kSawSinclairEntry3Score;
+ if (_scoringFlags.getFlag(kScoringSawWSCDirectoryFlag))
+ result += kSawWSCDirectoryScore;
+ if (_scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag))
+ result += kUsedCrowBarInWSCScore;
+ if (_scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag))
+ result += kFinishedPlasmaDodgeScore;
+ if (_scoringFlags.getFlag(kScoringOpenedCatwalkFlag))
+ result += kOpenedCatwalkScore;
+ if (_scoringFlags.getFlag(kScoringStoppedWSCRobotFlag))
+ result += kStoppedWSCRobotScore;
+ if (_scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag))
+ result += kGotWSCOpMemChipScore;
+ if (_scoringFlags.getFlag(kScoringFinishedWSCFlag))
+ result += kFinishedWSCScore;
+
+ return result;
+}
+
+GameScoreType GameStateManager::getGandhiScore() {
+ GameScoreType result = 0;
+
+ if (_scoringFlags.getFlag(kScoringMarsGandhiFlag))
+ result += kMarsGandhiScore;
+ if (_scoringFlags.getFlag(kScoringNoradGandhiFlag))
+ result += kNoradGandhiScore;
+ if (_scoringFlags.getFlag(kScoringWSCGandhiFlag))
+ result += kWSCGandhiScore;
+
+ return result;
+}
+
+GameScoreType GameStateManager::getTotalScore() {
+ return getCaldoriaTSAScore() +
+ getPrehistoricScore() +
+ getMarsScore() +
+ getNoradScore() +
+ getWSCScore() +
+ getGandhiScore();
+}
+
+/////////////////////////////////////////////
+//
+// Caldoria data
+
+void GameStateManager::writeCaldoriaState(Common::WriteStream *stream) {
+ _caldoriaFlags.writeToStream(stream);
+ stream->writeUint32BE(_caldoriaFuseTimeLimit);
+}
+
+void GameStateManager::readCaldoriaState(Common::ReadStream *stream) {
+ _caldoriaFlags.readFromStream(stream);
+ _caldoriaFuseTimeLimit = stream->readUint32BE();
+}
+
+void GameStateManager::resetCaldoriaState() {
+ _caldoriaFlags.clearAllFlags();
+ _caldoriaFuseTimeLimit = 0;
+}
+
+/////////////////////////////////////////////
+//
+// TSA data
+
+void GameStateManager::writeTSAState(Common::WriteStream *stream) {
+ _TSAFlags.writeToStream(stream);
+ stream->writeUint32BE(_TSARipTimerTime);
+ stream->writeUint32BE(_TSAFuseTimeLimit);
+ stream->writeByte(_TSAState);
+ stream->writeByte(_T0BMonitorMode);
+ stream->writeUint32BE(_T0BMonitorStart);
+}
+
+void GameStateManager::readTSAState(Common::ReadStream *stream) {
+ _TSAFlags.readFromStream(stream);
+ _TSARipTimerTime = stream->readUint32BE();
+ _TSAFuseTimeLimit = stream->readUint32BE();
+ _TSAState = stream->readByte();
+ _T0BMonitorMode = stream->readByte();
+ _T0BMonitorStart = stream->readUint32BE();
+}
+
+void GameStateManager::resetTSAState() {
+ _TSAFlags.clearAllFlags();
+ _TSAState = 0;
+ _T0BMonitorMode = 0;
+ _T0BMonitorStart = 0;
+ _TSARipTimerTime = 0;
+ _TSAFuseTimeLimit = kTSAUncreatedTimeLimit;
+}
+
+/////////////////////////////////////////////
+//
+// Prehistoric data
+
+void GameStateManager::writePrehistoricState(Common::WriteStream *stream) {
+ _prehistoricFlags.writeToStream(stream);
+}
+
+void GameStateManager::readPrehistoricState(Common::ReadStream *stream) {
+ _prehistoricFlags.readFromStream(stream);
+}
+
+void GameStateManager::resetPrehistoricState() {
+ _prehistoricFlags.clearAllFlags();
+}
+
+/////////////////////////////////////////////
+//
+// Norad data
+
+void GameStateManager::writeNoradState(Common::WriteStream *stream) {
+ _noradFlags.writeToStream(stream);
+ stream->writeUint16BE(_noradSubRoomPressure);
+ stream->writeByte(_noradSubPrepState);
+}
+
+void GameStateManager::readNoradState(Common::ReadStream *stream) {
+ _noradFlags.readFromStream(stream);
+ _noradSubRoomPressure = stream->readUint16BE();
+ _noradSubPrepState = (NoradSubPrepState)stream->readByte();
+}
+
+void GameStateManager::resetNoradState() {
+ _noradFlags.clearAllFlags();
+ _noradSubRoomPressure = 9;
+ _noradSubPrepState = kSubNotPrepped;
+}
+
+/////////////////////////////////////////////
+//
+// Mars data
+
+void GameStateManager::writeMarsState(Common::WriteStream *stream) {
+ _marsFlags.writeToStream(stream);
+}
+
+void GameStateManager::readMarsState(Common::ReadStream *stream) {
+ _marsFlags.readFromStream(stream);
+}
+
+void GameStateManager::resetMarsState() {
+ _marsFlags.clearAllFlags();
+}
+
+/////////////////////////////////////////////
+//
+// WSC data
+
+void GameStateManager::writeWSCState(Common::WriteStream *stream) {
+ _WSCFlags.writeToStream(stream);
+}
+
+void GameStateManager::readWSCState(Common::ReadStream *stream) {
+ _WSCFlags.readFromStream(stream);
+}
+
+void GameStateManager::resetWSCState() {
+ _WSCFlags.clearAllFlags();
+}
+
+void GameStateManager::setScoringSawINN(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawINNFlag, flag);
+}
+
+void GameStateManager::setScoringTookShower(const bool flag) {
+ _scoringFlags.setFlag(kScoringTookShowerFlag, flag);
+}
+
+void GameStateManager::setScoringFixedHair(const bool flag) {
+ _scoringFlags.setFlag(kScoringFixedHairFlag, flag);
+}
+
+void GameStateManager::setScoringGotKeyCard(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotKeyCardFlag, flag);
+}
+
+void GameStateManager::setScoringReadPaper(const bool flag) {
+ _scoringFlags.setFlag(kScoringReadPaperFlag, flag);
+}
+
+void GameStateManager::setScoringLookThroughTelescope(const bool flag) {
+ _scoringFlags.setFlag(kScoringLookThroughTelescopeFlag, flag);
+}
+
+void GameStateManager::setScoringSawCaldoriaKiosk(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawCaldoriaKioskFlag, flag);
+}
+
+void GameStateManager::setScoringGoToTSA(const bool flag) {
+ _scoringFlags.setFlag(kScoringGoToTSAFlag, flag);
+}
+
+void GameStateManager::setScoringEnterTSA(const bool flag) {
+ _scoringFlags.setFlag(kScoringEnterTSAFlag, flag);
+}
+
+void GameStateManager::setScoringSawBust1(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawBust1Flag, flag);
+}
+
+void GameStateManager::setScoringSawBust2(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawBust2Flag, flag);
+}
+
+void GameStateManager::setScoringSawBust3(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawBust3Flag, flag);
+}
+
+void GameStateManager::setScoringSawBust4(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawBust4Flag, flag);
+}
+
+void GameStateManager::setScoringSawBust5(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawBust5Flag, flag);
+}
+
+void GameStateManager::setScoringSawBust6(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawBust6Flag, flag);
+}
+
+void GameStateManager::setScoringSawTheory(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawTheoryFlag, flag);
+}
+
+void GameStateManager::setScoringSawBackground(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawBackgroundFlag, flag);
+}
+
+void GameStateManager::setScoringSawProcedure(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawProcedureFlag, flag);
+}
+
+void GameStateManager::setScoringGotJourneymanKey(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotJourneymanKeyFlag, flag);
+}
+
+void GameStateManager::setScoringGotPegasusBiochip(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotPegasusBiochipFlag, flag);
+}
+
+void GameStateManager::setScoringGotBiosuit(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotBiosuitFlag, flag);
+}
+
+void GameStateManager::setScoringGoToPrehistoric(const bool flag) {
+ _scoringFlags.setFlag(kScoringGoToPrehistoricFlag, flag);
+}
+
+void GameStateManager::setScoringPutLogInReader(const bool flag) {
+ _scoringFlags.setFlag(kScoringPutLogInReaderFlag, flag);
+}
+
+void GameStateManager::setScoringSawCaldoriaNormal(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawCaldoriaNormalFlag, flag);
+}
+
+void GameStateManager::setScoringSawCaldoriaAltered(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawCaldoriaAlteredFlag, flag);
+}
+
+void GameStateManager::setScoringSawNoradNormal(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawNoradNormalFlag, flag);
+}
+
+void GameStateManager::setScoringSawNoradAltered(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawNoradAlteredFlag, flag);
+}
+
+void GameStateManager::setScoringSawMarsNormal(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawMarsNormalFlag, flag);
+}
+
+void GameStateManager::setScoringSawMarsAltered(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawMarsAlteredFlag, flag);
+}
+
+void GameStateManager::setScoringSawWSCNormal(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawWSCNormalFlag, flag);
+}
+
+void GameStateManager::setScoringSawWSCAltered(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawWSCAlteredFlag, flag);
+}
+
+void GameStateManager::setScoringWentToReadyRoom2(const bool flag) {
+ _scoringFlags.setFlag(kScoringWentToReadyRoom2Flag, flag);
+}
+
+void GameStateManager::setScoringWentAfterSinclair(const bool flag) {
+ _scoringFlags.setFlag(kScoringWentAfterSinclairFlag, flag);
+}
+
+void GameStateManager::setScoringUsedCardBomb(const bool flag) {
+ _scoringFlags.setFlag(kScoringUsedCardBombFlag, flag);
+}
+
+void GameStateManager::setScoringShieldedCardBomb(const bool flag) {
+ _scoringFlags.setFlag(kScoringShieldedCardBombFlag, flag);
+}
+
+void GameStateManager::setScoringStunnedSinclair(const bool flag) {
+ _scoringFlags.setFlag(kScoringStunnedSinclairFlag, flag);
+}
+
+void GameStateManager::setScoringDisarmedNuke(const bool flag) {
+ _scoringFlags.setFlag(kScoringDisarmedNukeFlag, flag);
+}
+
+void GameStateManager::setScoringThrewBreaker(const bool flag) {
+ _scoringFlags.setFlag(kScoringThrewBreakerFlag, flag);
+}
+
+void GameStateManager::setScoringExtendedBridge(const bool flag) {
+ _scoringFlags.setFlag(kScoringExtendedBridgeFlag, flag);
+}
+
+void GameStateManager::setScoringGotHistoricalLog(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotHistoricalLogFlag, flag);
+}
+
+void GameStateManager::setScoringFinishedPrehistoric(const bool flag) {
+ _scoringFlags.setFlag(kScoringFinishedPrehistoricFlag, flag);
+}
+
+void GameStateManager::setScoringThrownByRobot(const bool flag) {
+ _scoringFlags.setFlag(kScoringThrownByRobotFlag, flag);
+}
+
+void GameStateManager::setScoringGotMarsCard(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotMarsCardFlag, flag);
+}
+
+void GameStateManager::setScoringSawMarsKiosk(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawMarsKioskFlag, flag);
+}
+
+void GameStateManager::setScoringSawTransportMap(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawTransportMapFlag, flag);
+}
+
+void GameStateManager::setScoringGotCrowBar(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotCrowBarFlag, flag);
+}
+
+void GameStateManager::setScoringTurnedOnTransport(const bool flag) {
+ _scoringFlags.setFlag(kScoringTurnedOnTransportFlag, flag);
+}
+
+void GameStateManager::setScoringGotOxygenMask(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotOxygenMaskFlag, flag);
+}
+
+void GameStateManager::setScoringAvoidedRobot(const bool flag) {
+ _scoringFlags.setFlag(kScoringAvoidedRobotFlag, flag);
+}
+
+void GameStateManager::setScoringActivatedPlatform(const bool flag) {
+ _scoringFlags.setFlag(kScoringActivatedPlatformFlag, flag);
+}
+
+void GameStateManager::setScoringUsedLiquidNitrogen(const bool flag) {
+ _scoringFlags.setFlag(kScoringUsedLiquidNitrogenFlag, flag);
+}
+
+void GameStateManager::setScoringUsedCrowBar(const bool flag) {
+ _scoringFlags.setFlag(kScoringUsedCrowBarFlag, flag);
+}
+
+void GameStateManager::setScoringFoundCardBomb(const bool flag) {
+ _scoringFlags.setFlag(kScoringFoundCardBombFlag, flag);
+}
+
+void GameStateManager::setScoringDisarmedCardBomb(const bool flag) {
+ _scoringFlags.setFlag(kScoringDisarmedCardBombFlag, flag);
+}
+
+void GameStateManager::setScoringGotCardBomb(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotCardBombFlag, flag);
+}
+
+void GameStateManager::setScoringThreadedMaze(const bool flag) {
+ _scoringFlags.setFlag(kScoringThreadedMazeFlag, flag);
+}
+
+void GameStateManager::setScoringThreadedGearRoom(const bool flag) {
+ _scoringFlags.setFlag(kScoringThreadedGearRoomFlag, flag);
+}
+
+void GameStateManager::setScoringEnteredShuttle(const bool flag) {
+ _scoringFlags.setFlag(kScoringEnteredShuttleFlag, flag);
+}
+
+void GameStateManager::setScoringEnteredLaunchTube(const bool flag) {
+ _scoringFlags.setFlag(kScoringEnteredLaunchTubeFlag, flag);
+}
+
+void GameStateManager::setScoringStoppedRobotsShuttle(const bool flag) {
+ _scoringFlags.setFlag(kScoringStoppedRobotsShuttleFlag, flag);
+}
+
+void GameStateManager::setScoringGotMarsOpMemChip(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotMarsOpMemChipFlag, flag);
+}
+
+void GameStateManager::setScoringFinishedMars(const bool flag) {
+ _scoringFlags.setFlag(kScoringFinishedMarsFlag, flag);
+}
+
+void GameStateManager::setScoringSawSecurityMonitor(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawSecurityMonitorFlag, flag);
+}
+
+void GameStateManager::setScoringFilledOxygenCanister(const bool flag) {
+ _scoringFlags.setFlag(kScoringFilledOxygenCanisterFlag, flag);
+}
+
+void GameStateManager::setScoringFilledArgonCanister(const bool flag) {
+ _scoringFlags.setFlag(kScoringFilledArgonCanisterFlag, flag);
+}
+
+void GameStateManager::setScoringSawUnconsciousOperator(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawUnconsciousOperatorFlag, flag);
+}
+
+void GameStateManager::setScoringWentThroughPressureDoor(const bool flag) {
+ _scoringFlags.setFlag(kScoringWentThroughPressureDoorFlag, flag);
+}
+
+void GameStateManager::setScoringPreppedSub(const bool flag) {
+ _scoringFlags.setFlag(kScoringPreppedSubFlag, flag);
+}
+
+void GameStateManager::setScoringEnteredSub(const bool flag) {
+ _scoringFlags.setFlag(kScoringEnteredSubFlag, flag);
+}
+
+void GameStateManager::setScoringExitedSub(const bool flag) {
+ _scoringFlags.setFlag(kScoringExitedSubFlag, flag);
+}
+
+void GameStateManager::setScoringSawRobotAt54North(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawRobotAt54NorthFlag, flag);
+}
+
+void GameStateManager::setScoringPlayedWithClaw(const bool flag) {
+ _scoringFlags.setFlag(kScoringPlayedWithClawFlag, flag);
+}
+
+void GameStateManager::setScoringUsedRetinalChip(const bool flag) {
+ _scoringFlags.setFlag(kScoringUsedRetinalChipFlag, flag);
+}
+
+void GameStateManager::setScoringFinishedGlobeGame(const bool flag) {
+ _scoringFlags.setFlag(kScoringFinishedGlobeGameFlag, flag);
+}
+
+void GameStateManager::setScoringStoppedNoradRobot(const bool flag) {
+ _scoringFlags.setFlag(kScoringStoppedNoradRobotFlag, flag);
+}
+
+void GameStateManager::setScoringGotNoradOpMemChip(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotNoradOpMemChipFlag, flag);
+}
+
+void GameStateManager::setScoringFinishedNorad(const bool flag) {
+ _scoringFlags.setFlag(kScoringFinishedNoradFlag, flag);
+}
+
+void GameStateManager::setScoringRemovedDart(const bool flag) {
+ _scoringFlags.setFlag(kScoringRemovedDartFlag, flag);
+}
+
+void GameStateManager::setScoringAnalyzedDart(const bool flag) {
+ _scoringFlags.setFlag(kScoringAnalyzedDartFlag, flag);
+}
+
+void GameStateManager::setScoringBuiltAntidote(const bool flag) {
+ _scoringFlags.setFlag(kScoringBuiltAntidoteFlag, flag);
+}
+
+void GameStateManager::setScoringGotSinclairKey(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotSinclairKeyFlag, flag);
+}
+
+void GameStateManager::setScoringGotArgonCanister(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotArgonCanisterFlag, flag);
+}
+
+void GameStateManager::setScoringGotNitrogenCanister(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotNitrogenCanisterFlag, flag);
+}
+
+void GameStateManager::setScoringPlayedWithMessages(const bool flag) {
+ _scoringFlags.setFlag(kScoringPlayedWithMessagesFlag, flag);
+}
+
+void GameStateManager::setScoringSawMorphExperiment(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawMorphExperimentFlag, flag);
+}
+
+void GameStateManager::setScoringEnteredSinclairOffice(const bool flag) {
+ _scoringFlags.setFlag(kScoringEnteredSinclairOfficeFlag, flag);
+}
+
+void GameStateManager::setScoringSawBrochure(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawBrochureFlag, flag);
+}
+
+void GameStateManager::setScoringSawSinclairEntry1(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawSinclairEntry1Flag, flag);
+}
+
+void GameStateManager::setScoringSawSinclairEntry2(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawSinclairEntry2Flag, flag);
+}
+
+void GameStateManager::setScoringSawSinclairEntry3(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawSinclairEntry3Flag, flag);
+}
+
+void GameStateManager::setScoringSawWSCDirectory(const bool flag) {
+ _scoringFlags.setFlag(kScoringSawWSCDirectoryFlag, flag);
+}
+
+void GameStateManager::setScoringUsedCrowBarInWSC(const bool flag) {
+ _scoringFlags.setFlag(kScoringUsedCrowBarInWSCFlag, flag);
+}
+
+void GameStateManager::setScoringFinishedPlasmaDodge(const bool flag) {
+ _scoringFlags.setFlag(kScoringFinishedPlasmaDodgeFlag, flag);
+}
+
+void GameStateManager::setScoringOpenedCatwalk(const bool flag) {
+ _scoringFlags.setFlag(kScoringOpenedCatwalkFlag, flag);
+}
+
+void GameStateManager::setScoringStoppedWSCRobot(const bool flag) {
+ _scoringFlags.setFlag(kScoringStoppedWSCRobotFlag, flag);
+}
+
+void GameStateManager::setScoringGotWSCOpMemChip(const bool flag) {
+ _scoringFlags.setFlag(kScoringGotWSCOpMemChipFlag, flag);
+}
+
+void GameStateManager::setScoringFinishedWSC(const bool flag) {
+ _scoringFlags.setFlag(kScoringFinishedWSCFlag, flag);
+}
+
+void GameStateManager::setScoringMarsGandhi(const bool flag) {
+ _scoringFlags.setFlag(kScoringMarsGandhiFlag, flag);
+}
+
+void GameStateManager::setScoringNoradGandhi(const bool flag) {
+ _scoringFlags.setFlag(kScoringNoradGandhiFlag, flag);
+}
+
+void GameStateManager::setScoringWSCGandhi(const bool flag) {
+ _scoringFlags.setFlag(kScoringWSCGandhiFlag, flag);
+}
+
+bool GameStateManager::getScoringSawINN() {
+ return _scoringFlags.getFlag(kScoringSawINNFlag);
+}
+
+bool GameStateManager::getScoringTookShower() {
+ return _scoringFlags.getFlag(kScoringTookShowerFlag);
+}
+
+bool GameStateManager::getScoringFixedHair() {
+ return _scoringFlags.getFlag(kScoringFixedHairFlag);
+}
+
+bool GameStateManager::getScoringGotKeyCard() {
+ return _scoringFlags.getFlag(kScoringGotKeyCardFlag);
+}
+
+bool GameStateManager::getScoringReadPaper() {
+ return _scoringFlags.getFlag(kScoringReadPaperFlag);
+}
+
+bool GameStateManager::getScoringLookThroughTelescope() {
+ return _scoringFlags.getFlag(kScoringLookThroughTelescopeFlag);
+}
+
+bool GameStateManager::getScoringSawCaldoriaKiosk() {
+ return _scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag);
+}
+
+bool GameStateManager::getScoringGoToTSA() {
+ return _scoringFlags.getFlag(kScoringGoToTSAFlag);
+}
+
+bool GameStateManager::getScoringEnterTSA() {
+ return _scoringFlags.getFlag(kScoringEnterTSAFlag);
+}
+
+bool GameStateManager::getScoringSawBust1() {
+ return _scoringFlags.getFlag(kScoringSawBust1Flag);
+}
+
+bool GameStateManager::getScoringSawBust2() {
+ return _scoringFlags.getFlag(kScoringSawBust2Flag);
+}
+
+bool GameStateManager::getScoringSawBust3() {
+ return _scoringFlags.getFlag(kScoringSawBust3Flag);
+}
+
+bool GameStateManager::getScoringSawBust4() {
+ return _scoringFlags.getFlag(kScoringSawBust4Flag);
+}
+
+bool GameStateManager::getScoringSawBust5() {
+ return _scoringFlags.getFlag(kScoringSawBust5Flag);
+}
+
+bool GameStateManager::getScoringSawBust6() {
+ return _scoringFlags.getFlag(kScoringSawBust6Flag);
+}
+
+bool GameStateManager::getScoringSawTheory() {
+ return _scoringFlags.getFlag(kScoringSawTheoryFlag);
+}
+
+bool GameStateManager::getScoringSawBackground() {
+ return _scoringFlags.getFlag(kScoringSawBackgroundFlag);
+}
+
+bool GameStateManager::getScoringSawProcedure() {
+ return _scoringFlags.getFlag(kScoringSawProcedureFlag);
+}
+
+bool GameStateManager::getScoringGotJourneymanKey() {
+ return _scoringFlags.getFlag(kScoringGotJourneymanKeyFlag);
+}
+
+bool GameStateManager::getScoringGotPegasusBiochip() {
+ return _scoringFlags.getFlag(kScoringGotPegasusBiochipFlag);
+}
+
+bool GameStateManager::getScoringGotBiosuit() {
+ return _scoringFlags.getFlag(kScoringGotBiosuitFlag);
+}
+
+bool GameStateManager::getScoringGoToPrehistoric() {
+ return _scoringFlags.getFlag(kScoringGoToPrehistoricFlag);
+}
+
+bool GameStateManager::getScoringPutLogInReader() {
+ return _scoringFlags.getFlag(kScoringPutLogInReaderFlag);
+}
+
+bool GameStateManager::getScoringSawCaldoriaNormal() {
+ return _scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag);
+}
+
+bool GameStateManager::getScoringSawCaldoriaAltered() {
+ return _scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag);
+}
+
+bool GameStateManager::getScoringSawNoradNormal() {
+ return _scoringFlags.getFlag(kScoringSawNoradNormalFlag);
+}
+
+bool GameStateManager::getScoringSawNoradAltered() {
+ return _scoringFlags.getFlag(kScoringSawNoradAlteredFlag);
+}
+
+bool GameStateManager::getScoringSawMarsNormal() {
+ return _scoringFlags.getFlag(kScoringSawMarsNormalFlag);
+}
+
+bool GameStateManager::getScoringSawMarsAltered() {
+ return _scoringFlags.getFlag(kScoringSawMarsAlteredFlag);
+}
+
+bool GameStateManager::getScoringSawWSCNormal() {
+ return _scoringFlags.getFlag(kScoringSawWSCNormalFlag);
+}
+
+bool GameStateManager::getScoringSawWSCAltered() {
+ return _scoringFlags.getFlag(kScoringSawWSCAlteredFlag);
+}
+
+bool GameStateManager::getScoringWentToReadyRoom2() {
+ return _scoringFlags.getFlag(kScoringWentToReadyRoom2Flag);
+}
+
+bool GameStateManager::getScoringWentAfterSinclair() {
+ return _scoringFlags.getFlag(kScoringWentAfterSinclairFlag);
+}
+
+bool GameStateManager::getScoringUsedCardBomb() {
+ return _scoringFlags.getFlag(kScoringUsedCardBombFlag);
+}
+
+bool GameStateManager::getScoringShieldedCardBomb() {
+ return _scoringFlags.getFlag(kScoringShieldedCardBombFlag);
+}
+
+bool GameStateManager::getScoringStunnedSinclair() {
+ return _scoringFlags.getFlag(kScoringStunnedSinclairFlag);
+}
+
+bool GameStateManager::getScoringDisarmedNuke() {
+ return _scoringFlags.getFlag(kScoringDisarmedNukeFlag);
+}
+
+bool GameStateManager::getScoringThrewBreaker() {
+ return _scoringFlags.getFlag(kScoringThrewBreakerFlag);
+}
+
+bool GameStateManager::getScoringExtendedBridge() {
+ return _scoringFlags.getFlag(kScoringExtendedBridgeFlag);
+}
+
+bool GameStateManager::getScoringGotHistoricalLog() {
+ return _scoringFlags.getFlag(kScoringGotHistoricalLogFlag);
+}
+
+bool GameStateManager::getScoringFinishedPrehistoric() {
+ return _scoringFlags.getFlag(kScoringFinishedPrehistoricFlag);
+}
+
+bool GameStateManager::getScoringThrownByRobot() {
+ return _scoringFlags.getFlag(kScoringThrownByRobotFlag);
+}
+
+bool GameStateManager::getScoringGotMarsCard() {
+ return _scoringFlags.getFlag(kScoringGotMarsCardFlag);
+}
+
+bool GameStateManager::getScoringSawMarsKiosk() {
+ return _scoringFlags.getFlag(kScoringSawMarsKioskFlag);
+}
+
+bool GameStateManager::getScoringSawTransportMap() {
+ return _scoringFlags.getFlag(kScoringSawTransportMapFlag);
+}
+
+bool GameStateManager::getScoringGotCrowBar() {
+ return _scoringFlags.getFlag(kScoringGotCrowBarFlag);
+}
+
+bool GameStateManager::getScoringTurnedOnTransport() {
+ return _scoringFlags.getFlag(kScoringTurnedOnTransportFlag);
+}
+
+bool GameStateManager::getScoringGotOxygenMask() {
+ return _scoringFlags.getFlag(kScoringGotOxygenMaskFlag);
+}
+
+bool GameStateManager::getScoringAvoidedRobot() {
+ return _scoringFlags.getFlag(kScoringAvoidedRobotFlag);
+}
+
+bool GameStateManager::getScoringActivatedPlatform() {
+ return _scoringFlags.getFlag(kScoringActivatedPlatformFlag);
+}
+
+bool GameStateManager::getScoringUsedLiquidNitrogen() {
+ return _scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag);
+}
+
+bool GameStateManager::getScoringUsedCrowBar() {
+ return _scoringFlags.getFlag(kScoringUsedCrowBarFlag);
+}
+
+bool GameStateManager::getScoringFoundCardBomb() {
+ return _scoringFlags.getFlag(kScoringFoundCardBombFlag);
+}
+
+bool GameStateManager::getScoringDisarmedCardBomb() {
+ return _scoringFlags.getFlag(kScoringDisarmedCardBombFlag);
+}
+
+bool GameStateManager::getScoringGotCardBomb() {
+ return _scoringFlags.getFlag(kScoringGotCardBombFlag);
+}
+
+bool GameStateManager::getScoringThreadedMaze() {
+ return _scoringFlags.getFlag(kScoringThreadedMazeFlag);
+}
+
+bool GameStateManager::getScoringThreadedGearRoom() {
+ return _scoringFlags.getFlag(kScoringThreadedGearRoomFlag);
+}
+
+bool GameStateManager::getScoringEnteredShuttle() {
+ return _scoringFlags.getFlag(kScoringEnteredShuttleFlag);
+}
+
+bool GameStateManager::getScoringEnteredLaunchTube() {
+ return _scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag);
+}
+
+bool GameStateManager::getScoringStoppedRobotsShuttle() {
+ return _scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag);
+}
+
+bool GameStateManager::getScoringGotMarsOpMemChip() {
+ return _scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag);
+}
+
+bool GameStateManager::getScoringFinishedMars() {
+ return _scoringFlags.getFlag(kScoringFinishedMarsFlag);
+}
+
+bool GameStateManager::getScoringSawSecurityMonitor() {
+ return _scoringFlags.getFlag(kScoringSawSecurityMonitorFlag);
+}
+
+bool GameStateManager::getScoringFilledOxygenCanister() {
+ return _scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag);
+}
+
+bool GameStateManager::getScoringFilledArgonCanister() {
+ return _scoringFlags.getFlag(kScoringFilledArgonCanisterFlag);
+}
+
+bool GameStateManager::getScoringSawUnconsciousOperator() {
+ return _scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag);
+}
+
+bool GameStateManager::getScoringWentThroughPressureDoor() {
+ return _scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag);
+}
+
+bool GameStateManager::getScoringPreppedSub() {
+ return _scoringFlags.getFlag(kScoringPreppedSubFlag);
+}
+
+bool GameStateManager::getScoringEnteredSub() {
+ return _scoringFlags.getFlag(kScoringEnteredSubFlag);
+}
+
+bool GameStateManager::getScoringExitedSub() {
+ return _scoringFlags.getFlag(kScoringExitedSubFlag);
+}
+
+bool GameStateManager::getScoringSawRobotAt54North() {
+ return _scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag);
+}
+
+bool GameStateManager::getScoringPlayedWithClaw() {
+ return _scoringFlags.getFlag(kScoringPlayedWithClawFlag);
+}
+
+bool GameStateManager::getScoringUsedRetinalChip() {
+ return _scoringFlags.getFlag(kScoringUsedRetinalChipFlag);
+}
+
+bool GameStateManager::getScoringFinishedGlobeGame() {
+ return _scoringFlags.getFlag(kScoringFinishedGlobeGameFlag);
+}
+
+bool GameStateManager::getScoringStoppedNoradRobot() {
+ return _scoringFlags.getFlag(kScoringStoppedNoradRobotFlag);
+}
+
+bool GameStateManager::getScoringGotNoradOpMemChip() {
+ return _scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag);
+}
+
+bool GameStateManager::getScoringFinishedNorad() {
+ return _scoringFlags.getFlag(kScoringFinishedNoradFlag);
+}
+
+bool GameStateManager::getScoringRemovedDart() {
+ return _scoringFlags.getFlag(kScoringRemovedDartFlag);
+}
+
+bool GameStateManager::getScoringAnalyzedDart() {
+ return _scoringFlags.getFlag(kScoringAnalyzedDartFlag);
+}
+
+bool GameStateManager::getScoringBuiltAntidote() {
+ return _scoringFlags.getFlag(kScoringBuiltAntidoteFlag);
+}
+
+bool GameStateManager::getScoringGotSinclairKey() {
+ return _scoringFlags.getFlag(kScoringGotSinclairKeyFlag);
+}
+
+bool GameStateManager::getScoringGotArgonCanister() {
+ return _scoringFlags.getFlag(kScoringGotArgonCanisterFlag);
+}
+
+bool GameStateManager::getScoringGotNitrogenCanister() {
+ return _scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag);
+}
+
+bool GameStateManager::getScoringPlayedWithMessages() {
+ return _scoringFlags.getFlag(kScoringPlayedWithMessagesFlag);
+}
+
+bool GameStateManager::getScoringSawMorphExperiment() {
+ return _scoringFlags.getFlag(kScoringSawMorphExperimentFlag);
+}
+
+bool GameStateManager::getScoringEnteredSinclairOffice() {
+ return _scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag);
+}
+
+bool GameStateManager::getScoringSawBrochure() {
+ return _scoringFlags.getFlag(kScoringSawBrochureFlag);
+}
+
+bool GameStateManager::getScoringSawSinclairEntry1() {
+ return _scoringFlags.getFlag(kScoringSawSinclairEntry1Flag);
+}
+
+bool GameStateManager::getScoringSawSinclairEntry2() {
+ return _scoringFlags.getFlag(kScoringSawSinclairEntry2Flag);
+}
+
+bool GameStateManager::getScoringSawSinclairEntry3() {
+ return _scoringFlags.getFlag(kScoringSawSinclairEntry3Flag);
+}
+
+bool GameStateManager::getScoringSawWSCDirectory() {
+ return _scoringFlags.getFlag(kScoringSawWSCDirectoryFlag);
+}
+
+bool GameStateManager::getScoringUsedCrowBarInWSC() {
+ return _scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag);
+}
+
+bool GameStateManager::getScoringFinishedPlasmaDodge() {
+ return _scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag);
+}
+
+bool GameStateManager::getScoringOpenedCatwalk() {
+ return _scoringFlags.getFlag(kScoringOpenedCatwalkFlag);
+}
+
+bool GameStateManager::getScoringStoppedWSCRobot() {
+ return _scoringFlags.getFlag(kScoringStoppedWSCRobotFlag);
+}
+
+bool GameStateManager::getScoringGotWSCOpMemChip() {
+ return _scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag);
+}
+
+bool GameStateManager::getScoringFinishedWSC() {
+ return _scoringFlags.getFlag(kScoringFinishedWSCFlag);
+}
+
+bool GameStateManager::getScoringMarsGandhi() {
+ return _scoringFlags.getFlag(kScoringMarsGandhiFlag);
+}
+
+bool GameStateManager::getScoringNoradGandhi() {
+ return _scoringFlags.getFlag(kScoringNoradGandhiFlag);
+}
+
+bool GameStateManager::getScoringWSCGandhi() {
+ return _scoringFlags.getFlag(kScoringWSCGandhiFlag);
+}
+
+void GameStateManager::setWalkthroughMode(bool value) {
+ _globalFlags.setFlag(kGlobalWalkthroughFlag, value);
+}
+
+bool GameStateManager::getWalkthroughMode() {
+ return _globalFlags.getFlag(kGlobalWalkthroughFlag);
+}
+
+void GameStateManager::setShieldOn(bool value) {
+ _globalFlags.setFlag(kGlobalShieldOnFlag, value);
+}
+
+bool GameStateManager::getShieldOn() {
+ return _globalFlags.getFlag(kGlobalShieldOnFlag);
+}
+
+void GameStateManager::setEasterEgg(bool value) {
+ _globalFlags.setFlag(kGlobalEasterEggFlag, value);
+}
+
+bool GameStateManager::getEasterEgg() {
+ return _globalFlags.getFlag(kGlobalEasterEggFlag);
+}
+
+void GameStateManager::setBeenToWSC(bool value) {
+ _globalFlags.setFlag(kGlobalBeenToWSCFlag, value);
+}
+
+bool GameStateManager::getBeenToWSC() {
+ return _globalFlags.getFlag(kGlobalBeenToWSCFlag);
+}
+
+void GameStateManager::setBeenToMars(bool value) {
+ _globalFlags.setFlag(kGlobalBeenToMarsFlag, value);
+}
+
+bool GameStateManager::getBeenToMars() {
+ return _globalFlags.getFlag(kGlobalBeenToMarsFlag);
+}
+
+void GameStateManager::setBeenToNorad(bool value) {
+ _globalFlags.setFlag(kGlobalBeenToNoradFlag, value);
+}
+
+bool GameStateManager::getBeenToNorad() {
+ return _globalFlags.getFlag(kGlobalBeenToNoradFlag);
+}
+
+void GameStateManager::setWSCFinished(bool value) {
+ _globalFlags.setFlag(kGlobalWSCFinishedFlag, value);
+}
+
+bool GameStateManager::getWSCFinished() {
+ return _globalFlags.getFlag(kGlobalWSCFinishedFlag);
+}
+
+void GameStateManager::setMarsFinished(bool value) {
+ _globalFlags.setFlag(kGlobalMarsFinishedFlag, value);
+}
+
+bool GameStateManager::getMarsFinished() {
+ return _globalFlags.getFlag(kGlobalMarsFinishedFlag);
+}
+
+void GameStateManager::setNoradFinished(bool value) {
+ _globalFlags.setFlag(kGlobalNoradFinishedFlag, value);
+}
+
+bool GameStateManager::getNoradFinished() {
+ return _globalFlags.getFlag(kGlobalNoradFinishedFlag);
+}
+
+bool GameStateManager::allTimeZonesFinished() {
+ return getWSCFinished() && getMarsFinished() && getNoradFinished();
+}
+
+void GameStateManager::setTakenItemID(ItemID id, bool value) {
+ _itemTakenFlags.setFlag(id, value);
+}
+
+bool GameStateManager::isTakenItemID(ItemID id) {
+ return _itemTakenFlags.getFlag(id);
+}
+
+void GameStateManager::setTakenItem(Item *item, bool value) {
+ setTakenItemID(item->getObjectID(), value);
+}
+
+bool GameStateManager::isTakenItem(Item *item) {
+ return isTakenItemID(item->getObjectID());
+}
+
+void GameStateManager::setCaldoriaFuseTimeLimit(const TimeValue timeLimit) {
+ _caldoriaFuseTimeLimit = timeLimit;
+}
+
+TimeValue GameStateManager::getCaldoriaFuseTimeLimit() {
+ return _caldoriaFuseTimeLimit;
+}
+
+void GameStateManager::setCaldoriaSeenPullback(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaSeenPullbackFlag, value);
+}
+
+bool GameStateManager::getCaldoriaSeenPullback() {
+ return _caldoriaFlags.getFlag(kCaldoriaSeenPullbackFlag);
+}
+
+void GameStateManager::setCaldoriaMadeOJ(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaMadeOJFlag, value);
+}
+
+bool GameStateManager::getCaldoriaMadeOJ() {
+ return _caldoriaFlags.getFlag(kCaldoriaMadeOJFlag);
+}
+
+void GameStateManager::setCaldoriaWokenUp(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaWokenUpFlag, value);
+}
+
+bool GameStateManager::getCaldoriaWokenUp() {
+ return _caldoriaFlags.getFlag(kCaldoriaWokenUpFlag);
+}
+
+void GameStateManager::setCaldoriaDidRecalibration(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaDidRecalibrationFlag, value);
+}
+
+bool GameStateManager::getCaldoriaDidRecalibration() {
+ return _caldoriaFlags.getFlag(kCaldoriaDidRecalibrationFlag);
+}
+
+void GameStateManager::setCaldoriaSeenSinclairInElevator(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaSeenSinclairInElevatorFlag, value);
+}
+
+bool GameStateManager::getCaldoriaSeenSinclairInElevator() {
+ return _caldoriaFlags.getFlag(kCaldoriaSeenSinclairInElevatorFlag);
+}
+
+void GameStateManager::setCaldoriaINNAnnouncing(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaINNAnnouncingFlag, value);
+}
+
+bool GameStateManager::getCaldoriaINNAnnouncing() {
+ return _caldoriaFlags.getFlag(kCaldoriaINNAnnouncingFlag);
+}
+
+void GameStateManager::setCaldoriaSeenINN(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaSeenINNFlag, value);
+}
+
+bool GameStateManager::getCaldoriaSeenINN() {
+ return _caldoriaFlags.getFlag(kCaldoriaSeenINNFlag);
+}
+
+void GameStateManager::setCaldoriaSeenMessages(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaSeenMessagesFlag, value);
+}
+
+bool GameStateManager::getCaldoriaSeenMessages() {
+ return _caldoriaFlags.getFlag(kCaldoriaSeenMessagesFlag);
+}
+
+void GameStateManager::setCaldoriaSinclairShot(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaSinclairShotFlag, value);
+}
+
+bool GameStateManager::getCaldoriaSinclairShot() {
+ return _caldoriaFlags.getFlag(kCaldoriaSinclairShotFlag);
+}
+
+void GameStateManager::setCaldoriaBombDisarmed(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaBombDisarmedFlag, value);
+}
+
+bool GameStateManager::getCaldoriaBombDisarmed() {
+ return _caldoriaFlags.getFlag(kCaldoriaBombDisarmedFlag);
+}
+
+void GameStateManager::setCaldoriaRoofDoorOpen(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaRoofDoorOpenFlag, value);
+}
+
+bool GameStateManager::getCaldoriaRoofDoorOpen() {
+ return _caldoriaFlags.getFlag(kCaldoriaRoofDoorOpenFlag);
+}
+
+void GameStateManager::setCaldoriaDoneHygiene(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaDoneHygieneFlag, value);
+}
+
+bool GameStateManager::getCaldoriaDoneHygiene() {
+ return _caldoriaFlags.getFlag(kCaldoriaDoneHygieneFlag);
+}
+
+void GameStateManager::setCaldoriaSawVoiceAnalysis(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaSawVoiceAnalysisFlag, value);
+}
+
+bool GameStateManager::getCaldoriaSawVoiceAnalysis() {
+ return _caldoriaFlags.getFlag(kCaldoriaSawVoiceAnalysisFlag);
+}
+
+void GameStateManager::setCaldoriaDoorBombed(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaDoorBombedFlag, value);
+}
+
+bool GameStateManager::getCaldoriaDoorBombed() {
+ return _caldoriaFlags.getFlag(kCaldoriaDoorBombedFlag);
+}
+
+void GameStateManager::setCaldoriaGunAimed(bool value) {
+ _caldoriaFlags.setFlag(kCaldoriaGunAimedFlag, value);
+}
+
+bool GameStateManager::getCaldoriaGunAimed() {
+ return _caldoriaFlags.getFlag(kCaldoriaGunAimedFlag);
+}
+
+void GameStateManager::setRipTimerTime(TimeValue limit) {
+ _TSARipTimerTime = limit;
+}
+
+TimeValue GameStateManager::getRipTimerTime() {
+ return _TSARipTimerTime;
+}
+
+void GameStateManager::setTSAFuseTimeLimit(TimeValue limit) {
+ _TSAFuseTimeLimit = limit;
+}
+
+TimeValue GameStateManager::getTSAFuseTimeLimit() {
+ return _TSAFuseTimeLimit;
+}
+
+void GameStateManager::setTSAState(byte state) {
+ _TSAState = state;
+}
+
+byte GameStateManager::getTSAState() {
+ return _TSAState;
+}
+
+void GameStateManager::setT0BMonitorMode(byte mode) {
+ _T0BMonitorMode = mode;
+}
+
+byte GameStateManager::getT0BMonitorMode() {
+ return _T0BMonitorMode;
+}
+
+void GameStateManager::setT0BMonitorStart(TimeValue start) {
+ _T0BMonitorStart = start;
+}
+
+TimeValue GameStateManager::getT0BMonitorStart() {
+ return _T0BMonitorStart;
+}
+
+void GameStateManager::setTSAIDedAtDoor(bool value) {
+ _TSAFlags.setFlag(kTSAIDedAtDoorFlag, value);
+}
+
+bool GameStateManager::getTSAIDedAtDoor() {
+ return _TSAFlags.getFlag(kTSAIDedAtDoorFlag);
+}
+
+void GameStateManager::setTSA0BZoomedIn(bool value) {
+ _TSAFlags.setFlag(kTSA0BZoomedInFlag, value);
+}
+
+bool GameStateManager::getTSA0BZoomedIn() {
+ return _TSAFlags.getFlag(kTSA0BZoomedInFlag);
+}
+
+void GameStateManager::setTSAFrontDoorUnlockedOutside(bool value) {
+ _TSAFlags.setFlag(kTSAFrontDoorUnlockedOutsideFlag, value);
+}
+
+bool GameStateManager::getTSAFrontDoorUnlockedOutside() {
+ return _TSAFlags.getFlag(kTSAFrontDoorUnlockedOutsideFlag);
+}
+
+void GameStateManager::setTSAFrontDoorUnlockedInside(bool value) {
+ _TSAFlags.setFlag(kTSAFrontDoorUnlockedInsideFlag, value);
+}
+
+bool GameStateManager::getTSAFrontDoorUnlockedInside() {
+ return _TSAFlags.getFlag(kTSAFrontDoorUnlockedInsideFlag);
+}
+
+void GameStateManager::setTSASeenRobotGreeting(bool value) {
+ _TSAFlags.setFlag(kTSASeenRobotGreetingFlag, value);
+}
+
+bool GameStateManager::getTSASeenRobotGreeting() {
+ return _TSAFlags.getFlag(kTSASeenRobotGreetingFlag);
+}
+
+void GameStateManager::setTSASeenTheory(bool value) {
+ _TSAFlags.setFlag(kTSASeenTheoryFlag, value);
+}
+
+bool GameStateManager::getTSASeenTheory() {
+ return _TSAFlags.getFlag(kTSASeenTheoryFlag);
+}
+
+void GameStateManager::setTSASeenBackground(bool value) {
+ _TSAFlags.setFlag(kTSASeenBackgroundFlag, value);
+}
+
+bool GameStateManager::getTSASeenBackground() {
+ return _TSAFlags.getFlag(kTSASeenBackgroundFlag);
+}
+
+void GameStateManager::setTSASeenProcedure(bool value) {
+ _TSAFlags.setFlag(kTSASeenProcedureFlag, value);
+}
+
+bool GameStateManager::getTSASeenProcedure() {
+ return _TSAFlags.getFlag(kTSASeenProcedureFlag);
+}
+
+void GameStateManager::setTSASeenAgent3AtDoor(bool value) {
+ _TSAFlags.setFlag(kTSASeenAgent3AtDoorFlag, value);
+}
+
+bool GameStateManager::getTSASeenAgent3AtDoor() {
+ return _TSAFlags.getFlag(kTSASeenAgent3AtDoorFlag);
+}
+
+void GameStateManager::setTSACommandCenterLocked(bool value) {
+ _TSAFlags.setFlag(kTSACommandCenterLockedFlag, value);
+}
+
+bool GameStateManager::getTSACommandCenterLocked() {
+ return _TSAFlags.getFlag(kTSACommandCenterLockedFlag);
+}
+
+void GameStateManager::setTSASeenCaldoriaNormal(bool value) {
+ _TSAFlags.setFlag(kTSASeenCaldoriaNormalFlag, value);
+}
+
+bool GameStateManager::getTSASeenCaldoriaNormal() {
+ return _TSAFlags.getFlag(kTSASeenCaldoriaNormalFlag);
+}
+
+void GameStateManager::setTSASeenCaldoriaAltered(bool value) {
+ _TSAFlags.setFlag(kTSASeenCaldoriaAlteredFlag, value);
+}
+
+bool GameStateManager::getTSASeenCaldoriaAltered() {
+ return _TSAFlags.getFlag(kTSASeenCaldoriaAlteredFlag);
+}
+
+void GameStateManager::setTSASeenNoradNormal(bool value) {
+ _TSAFlags.setFlag(kTSASeenNoradNormalFlag, value);
+}
+
+bool GameStateManager::getTSASeenNoradNormal() {
+ return _TSAFlags.getFlag(kTSASeenNoradNormalFlag);
+}
+
+void GameStateManager::setTSASeenNoradAltered(bool value) {
+ _TSAFlags.setFlag(kTSASeenNoradAlteredFlag, value);
+}
+
+bool GameStateManager::getTSASeenNoradAltered() {
+ return _TSAFlags.getFlag(kTSASeenNoradAlteredFlag);
+}
+
+void GameStateManager::setTSASeenMarsNormal(bool value) {
+ _TSAFlags.setFlag(kTSASeenMarsNormalFlag, value);
+}
+
+bool GameStateManager::getTSASeenMarsNormal() {
+ return _TSAFlags.getFlag(kTSASeenMarsNormalFlag);
+}
+
+void GameStateManager::setTSASeenMarsAltered(bool value) {
+ _TSAFlags.setFlag(kTSASeenMarsAlteredFlag, value);
+}
+
+bool GameStateManager::getTSASeenMarsAltered() {
+ return _TSAFlags.getFlag(kTSASeenMarsAlteredFlag);
+}
+
+void GameStateManager::setTSASeenWSCNormal(bool value) {
+ _TSAFlags.setFlag(kTSASeenWSCNormalFlag, value);
+}
+
+bool GameStateManager::getTSASeenWSCNormal() {
+ return _TSAFlags.getFlag(kTSASeenWSCNormalFlag);
+}
+
+void GameStateManager::setTSASeenWSCAltered(bool value) {
+ _TSAFlags.setFlag(kTSASeenWSCAlteredFlag, value);
+}
+
+bool GameStateManager::getTSASeenWSCAltered() {
+ return _TSAFlags.getFlag(kTSASeenWSCAlteredFlag);
+}
+
+void GameStateManager::setTSABiosuitOn(bool value) {
+ _TSAFlags.setFlag(kTSABiosuitOnFlag, value);
+}
+
+bool GameStateManager::getTSABiosuitOn() {
+ return _TSAFlags.getFlag(kTSABiosuitOnFlag);
+}
+
+void GameStateManager::setPrehistoricTriedToExtendBridge(bool value) {
+ _prehistoricFlags.setFlag(kPrehistoricTriedToExtendBridgeFlag, value);
+}
+
+bool GameStateManager::getPrehistoricTriedToExtendBridge() {
+ return _prehistoricFlags.getFlag(kPrehistoricTriedToExtendBridgeFlag);
+}
+
+void GameStateManager::setPrehistoricSeenTimeStream(bool value) {
+ _prehistoricFlags.setFlag(kPrehistoricSeenTimeStreamFlag, value);
+}
+
+bool GameStateManager::getPrehistoricSeenTimeStream() {
+ return _prehistoricFlags.getFlag(kPrehistoricSeenTimeStreamFlag);
+}
+
+void GameStateManager::setPrehistoricSeenFlyer1(bool value) {
+ _prehistoricFlags.setFlag(kPrehistoricSeenFlyer1Flag, value);
+}
+
+bool GameStateManager::getPrehistoricSeenFlyer1() {
+ return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer1Flag);
+}
+
+void GameStateManager::setPrehistoricSeenFlyer2(bool value) {
+ _prehistoricFlags.setFlag(kPrehistoricSeenFlyer2Flag, value);
+}
+
+bool GameStateManager::getPrehistoricSeenFlyer2() {
+ return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer2Flag);
+}
+
+void GameStateManager::setPrehistoricSeenBridgeZoom(bool value) {
+ _prehistoricFlags.setFlag(kPrehistoricSeenBridgeZoomFlag, value);
+}
+
+bool GameStateManager::getPrehistoricSeenBridgeZoom() {
+ return _prehistoricFlags.getFlag(kPrehistoricSeenBridgeZoomFlag);
+}
+
+void GameStateManager::setPrehistoricBreakerThrown(bool value) {
+ _prehistoricFlags.setFlag(kPrehistoricBreakerThrownFlag, value);
+}
+
+bool GameStateManager::getPrehistoricBreakerThrown() {
+ return _prehistoricFlags.getFlag(kPrehistoricBreakerThrownFlag);
+}
+
+void GameStateManager::setNoradSeenTimeStream(bool value) {
+ _noradFlags.setFlag(kNoradSeenTimeStreamFlag, value);
+}
+
+bool GameStateManager::getNoradSeenTimeStream() {
+ return _noradFlags.getFlag(kNoradSeenTimeStreamFlag);
+}
+
+void GameStateManager::setNoradGassed(bool value) {
+ _noradFlags.setFlag(kNoradGassedFlag, value);
+}
+
+bool GameStateManager::getNoradGassed() {
+ return _noradFlags.getFlag(kNoradGassedFlag);
+}
+
+void GameStateManager::setNoradFillingStationOn(bool value) {
+ _noradFlags.setFlag(kNoradFillingStationOnFlag, value);
+}
+
+bool GameStateManager::getNoradFillingStationOn() {
+ return _noradFlags.getFlag(kNoradFillingStationOnFlag);
+}
+
+void GameStateManager::setNoradN22MessagePlayed(bool value) {
+ _noradFlags.setFlag(kNoradN22MessagePlayedFlag, value);
+}
+
+bool GameStateManager::getNoradN22MessagePlayed() {
+ return _noradFlags.getFlag(kNoradN22MessagePlayedFlag);
+}
+
+void GameStateManager::setNoradPlayedGlobeGame(bool value) {
+ _noradFlags.setFlag(kNoradPlayedGlobeGameFlag, value);
+}
+
+bool GameStateManager::getNoradPlayedGlobeGame() {
+ return _noradFlags.getFlag(kNoradPlayedGlobeGameFlag);
+}
+
+void GameStateManager::setNoradBeatRobotWithClaw(bool value) {
+ _noradFlags.setFlag(kNoradBeatRobotWithClawFlag, value);
+}
+
+bool GameStateManager::getNoradBeatRobotWithClaw() {
+ return _noradFlags.getFlag(kNoradBeatRobotWithClawFlag);
+}
+
+void GameStateManager::setNoradBeatRobotWithDoor(bool value) {
+ _noradFlags.setFlag(kNoradBeatRobotWithDoorFlag, value);
+}
+
+bool GameStateManager::getNoradBeatRobotWithDoor() {
+ return _noradFlags.getFlag(kNoradBeatRobotWithDoorFlag);
+}
+
+void GameStateManager::setNoradRetScanGood(bool value) {
+ _noradFlags.setFlag(kNoradRetScanGoodFlag, value);
+}
+
+bool GameStateManager::getNoradRetScanGood() {
+ return _noradFlags.getFlag(kNoradRetScanGoodFlag);
+}
+
+void GameStateManager::setNoradWaitingForLaser(bool value) {
+ _noradFlags.setFlag(kNoradWaitingForLaserFlag, value);
+}
+
+bool GameStateManager::getNoradWaitingForLaser() {
+ return _noradFlags.getFlag(kNoradWaitingForLaserFlag);
+}
+
+void GameStateManager::setNoradSubRoomPressure(uint16 pressure) {
+ _noradSubRoomPressure = pressure;
+}
+
+uint16 GameStateManager::getNoradSubRoomPressure() {
+ return _noradSubRoomPressure;
+}
+
+void GameStateManager::setNoradSubPrepState(NoradSubPrepState state) {
+ _noradSubPrepState = state;
+}
+
+NoradSubPrepState GameStateManager::getNoradSubPrepState() {
+ return _noradSubPrepState;
+}
+
+void GameStateManager::setNoradArrivedFromSub(bool value) {
+ _noradFlags.setFlag(kNoradArrivedFromSubFlag, value);
+}
+
+bool GameStateManager::getNoradArrivedFromSub() {
+ return _noradFlags.getFlag(kNoradArrivedFromSubFlag);
+}
+
+void GameStateManager::setMarsSeenTimeStream(bool value) {
+ _marsFlags.setFlag(kMarsSeenTimeStreamFlag, value);
+}
+
+bool GameStateManager::getMarsSeenTimeStream() {
+ return _marsFlags.getFlag(kMarsSeenTimeStreamFlag);
+}
+
+void GameStateManager::setMarsHeardUpperPodMessage(bool value) {
+ _marsFlags.setFlag(kMarsHeardUpperPodMessageFlag, value);
+}
+
+bool GameStateManager::getMarsHeardUpperPodMessage() {
+ return _marsFlags.getFlag(kMarsHeardUpperPodMessageFlag);
+}
+
+void GameStateManager::setMarsRobotThrownPlayer(bool value) {
+ _marsFlags.setFlag(kMarsRobotThrownPlayerFlag, value);
+}
+
+bool GameStateManager::getMarsRobotThrownPlayer() {
+ return _marsFlags.getFlag(kMarsRobotThrownPlayerFlag);
+}
+
+void GameStateManager::setMarsHeardCheckInMessage(bool value) {
+ _marsFlags.setFlag(kMarsHeardCheckInMessageFlag, value);
+}
+
+bool GameStateManager::getMarsHeardCheckInMessage() {
+ return _marsFlags.getFlag(kMarsHeardCheckInMessageFlag);
+}
+
+void GameStateManager::setMarsPodAtUpperPlatform(bool value) {
+ _marsFlags.setFlag(kMarsPodAtUpperPlatformFlag, value);
+}
+
+bool GameStateManager::getMarsPodAtUpperPlatform() {
+ return _marsFlags.getFlag(kMarsPodAtUpperPlatformFlag);
+}
+
+void GameStateManager::setMarsSeenThermalScan(bool value) {
+ _marsFlags.setFlag(kMarsSeenThermalScanFlag, value);
+}
+
+bool GameStateManager::getMarsSeenThermalScan() {
+ return _marsFlags.getFlag(kMarsSeenThermalScanFlag);
+}
+
+void GameStateManager::setMarsArrivedBelow(bool value) {
+ _marsFlags.setFlag(kMarsArrivedBelowFlag, value);
+}
+
+bool GameStateManager::getMarsArrivedBelow() {
+ return _marsFlags.getFlag(kMarsArrivedBelowFlag);
+}
+
+void GameStateManager::setMarsSeenRobotAtReactor(bool value) {
+ _marsFlags.setFlag(kMarsSeenRobotAtReactorFlag, value);
+}
+
+bool GameStateManager::getMarsSeenRobotAtReactor() {
+ return _marsFlags.getFlag(kMarsSeenRobotAtReactorFlag);
+}
+
+void GameStateManager::setMarsAvoidedReactorRobot(bool value) {
+ _marsFlags.setFlag(kMarsAvoidedReactorRobotFlag, value);
+}
+
+bool GameStateManager::getMarsAvoidedReactorRobot() {
+ return _marsFlags.getFlag(kMarsAvoidedReactorRobotFlag);
+}
+
+void GameStateManager::setMarsSecurityDown(bool value) {
+ _marsFlags.setFlag(kMarsSecurityDownFlag, value);
+}
+
+bool GameStateManager::getMarsSecurityDown() {
+ return _marsFlags.getFlag(kMarsSecurityDownFlag);
+}
+
+void GameStateManager::setMarsInAirlock(bool value) {
+ _marsFlags.setFlag(kMarsInAirlockFlag, value);
+}
+
+bool GameStateManager::getMarsInAirlock() {
+ return _marsFlags.getFlag(kMarsInAirlockFlag);
+}
+
+void GameStateManager::setMarsAirlockOpen(bool value) {
+ _marsFlags.setFlag(kMarsAirlockOpenFlag, value);
+}
+
+bool GameStateManager::getMarsAirlockOpen() {
+ return _marsFlags.getFlag(kMarsAirlockOpenFlag);
+}
+
+void GameStateManager::setMarsMaskOnFiller(bool value) {
+ _marsFlags.setFlag(kMarsMaskOnFillerFlag, value);
+}
+
+bool GameStateManager::getMarsMaskOnFiller() {
+ return _marsFlags.getFlag(kMarsMaskOnFillerFlag);
+}
+
+void GameStateManager::setMarsLockFrozen(bool value) {
+ _marsFlags.setFlag(kMarsLockFrozenFlag, value);
+}
+
+bool GameStateManager::getMarsLockFrozen() {
+ return _marsFlags.getFlag(kMarsLockFrozenFlag);
+}
+
+void GameStateManager::setMarsLockBroken(bool value) {
+ _marsFlags.setFlag(kMarsLockBrokenFlag, value);
+}
+
+bool GameStateManager::getMarsLockBroken() {
+ return _marsFlags.getFlag(kMarsLockBrokenFlag);
+}
+
+void GameStateManager::setMarsMazeDoorPair1(bool value) {
+ _marsFlags.setFlag(kMarsMazeDoorPair1Flag, value);
+}
+
+bool GameStateManager::getMarsMazeDoorPair1() {
+ return _marsFlags.getFlag(kMarsMazeDoorPair1Flag);
+}
+
+void GameStateManager::setMarsMazeDoorPair2(bool value) {
+ _marsFlags.setFlag(kMarsMazeDoorPair2Flag, value);
+}
+
+bool GameStateManager::getMarsMazeDoorPair2() {
+ return _marsFlags.getFlag(kMarsMazeDoorPair2Flag);
+}
+
+void GameStateManager::setMarsMazeDoorPair3(bool value) {
+ _marsFlags.setFlag(kMarsMazeDoorPair3Flag, value);
+}
+
+bool GameStateManager::getMarsMazeDoorPair3() {
+ return _marsFlags.getFlag(kMarsMazeDoorPair3Flag);
+}
+
+void GameStateManager::setMarsSawRobotLeave(bool value) {
+ _marsFlags.setFlag(kMarsSawRobotLeaveFlag, value);
+}
+
+bool GameStateManager::getMarsSawRobotLeave() {
+ return _marsFlags.getFlag(kMarsSawRobotLeaveFlag);
+}
+
+void GameStateManager::setMarsHitRobotWithCannon(bool flag) {
+ _marsFlags.setFlag(kMarsHitRobotWithCannonFlag, flag);
+}
+
+bool GameStateManager::getMarsHitRobotWithCannon() {
+ return _marsFlags.getFlag(kMarsHitRobotWithCannonFlag);
+}
+
+void GameStateManager::setMarsReadyForShuttleTransport(bool value) {
+ _marsFlags.setFlag(kMarsReadyForShuttleTransportFlag, value);
+}
+
+bool GameStateManager::getMarsReadyForShuttleTransport() {
+ return _marsFlags.getFlag(kMarsReadyForShuttleTransportFlag);
+}
+
+void GameStateManager::setMarsFinishedCanyonChase(bool flag) {
+ _marsFlags.setFlag(kMarsFinishedCanyonChaseFlag, flag);
+}
+
+bool GameStateManager::getMarsFinishedCanyonChase() {
+ return _marsFlags.getFlag(kMarsFinishedCanyonChaseFlag);
+}
+
+void GameStateManager::setMarsThreadedMaze(bool flag) {
+ _marsFlags.setFlag(kMarsThreadedMazeFlag, flag);
+}
+
+bool GameStateManager::getMarsThreadedMaze() {
+ return _marsFlags.getFlag(kMarsThreadedMazeFlag);
+}
+
+void GameStateManager::setWSCSeenTimeStream(bool value) {
+ _WSCFlags.setFlag(kWSCSeenTimeStreamFlag, value);
+}
+
+bool GameStateManager::getWSCSeenTimeStream() {
+ return _WSCFlags.getFlag(kWSCSeenTimeStreamFlag);
+}
+
+void GameStateManager::setWSCPoisoned(bool value) {
+ _WSCFlags.setFlag(kWSCPoisonedFlag, value);
+}
+
+bool GameStateManager::getWSCPoisoned() {
+ return _WSCFlags.getFlag(kWSCPoisonedFlag);
+}
+
+void GameStateManager::setWSCAnsweredAboutDart(bool value) {
+ _WSCFlags.setFlag(kWSCAnsweredAboutDartFlag, value);
+}
+
+bool GameStateManager::getWSCAnsweredAboutDart() {
+ return _WSCFlags.getFlag(kWSCAnsweredAboutDartFlag);
+}
+
+void GameStateManager::setWSCRemovedDart(bool value) {
+ _WSCFlags.setFlag(kWSCRemovedDartFlag, value);
+}
+
+bool GameStateManager::getWSCRemovedDart() {
+ return _WSCFlags.getFlag(kWSCRemovedDartFlag);
+}
+
+void GameStateManager::setWSCAnalyzerOn(bool value) {
+ _WSCFlags.setFlag(kWSCAnalyzerOnFlag, value);
+}
+
+bool GameStateManager::getWSCAnalyzerOn() {
+ return _WSCFlags.getFlag(kWSCAnalyzerOnFlag);
+}
+
+void GameStateManager::setWSCDartInAnalyzer(bool value) {
+ _WSCFlags.setFlag(kWSCDartInAnalyzerFlag, value);
+}
+
+bool GameStateManager::getWSCDartInAnalyzer() {
+ return _WSCFlags.getFlag(kWSCDartInAnalyzerFlag);
+}
+
+void GameStateManager::setWSCAnalyzedDart(bool value) {
+ _WSCFlags.setFlag(kWSCAnalyzedDartFlag, value);
+}
+
+bool GameStateManager::getWSCAnalyzedDart() {
+ return _WSCFlags.getFlag(kWSCAnalyzedDartFlag);
+}
+
+void GameStateManager::setWSCSawMorph(bool value) {
+ _WSCFlags.setFlag(kWSCSawMorphFlag, value);
+}
+
+bool GameStateManager::getWSCSawMorph() {
+ return _WSCFlags.getFlag(kWSCSawMorphFlag);
+}
+
+void GameStateManager::setWSCDesignedAntidote(bool value) {
+ _WSCFlags.setFlag(kWSCDesignedAntidoteFlag, value);
+}
+
+bool GameStateManager::getWSCDesignedAntidote() {
+ return _WSCFlags.getFlag(kWSCDesignedAntidoteFlag);
+}
+
+void GameStateManager::setWSCPickedUpAntidote(bool value) {
+ _WSCFlags.setFlag(kWSCPickedUpAntidoteFlag, value);
+}
+
+bool GameStateManager::getWSCPickedUpAntidote() {
+ return _WSCFlags.getFlag(kWSCPickedUpAntidoteFlag);
+}
+
+void GameStateManager::setWSCOfficeMessagesOpen(bool value) {
+ _WSCFlags.setFlag(kWSCOfficeMessagesOpenFlag, value);
+}
+
+bool GameStateManager::getWSCOfficeMessagesOpen() {
+ return _WSCFlags.getFlag(kWSCOfficeMessagesOpenFlag);
+}
+
+void GameStateManager::setWSCSeenNerd(bool value) {
+ _WSCFlags.setFlag(kWSCSeenNerdFlag, value);
+}
+
+bool GameStateManager::getWSCSeenNerd() {
+ return _WSCFlags.getFlag(kWSCSeenNerdFlag);
+}
+
+void GameStateManager::setWSCHeardPage1(bool value) {
+ _WSCFlags.setFlag(kWSCHeardPage1Flag, value);
+}
+
+bool GameStateManager::getWSCHeardPage1() {
+ return _WSCFlags.getFlag(kWSCHeardPage1Flag);
+}
+
+void GameStateManager::setWSCHeardPage2(bool value) {
+ _WSCFlags.setFlag(kWSCHeardPage2Flag, value);
+}
+
+bool GameStateManager::getWSCHeardPage2() {
+ return _WSCFlags.getFlag(kWSCHeardPage2Flag);
+}
+
+void GameStateManager::setWSCHeardCheckIn(bool value) {
+ _WSCFlags.setFlag(kWSCHeardCheckInFlag, value);
+}
+
+bool GameStateManager::getWSCHeardCheckIn() {
+ return _WSCFlags.getFlag(kWSCHeardCheckInFlag);
+}
+
+void GameStateManager::setWSCDidPlasmaDodge(bool value) {
+ _WSCFlags.setFlag(kWSCDidPlasmaDodgeFlag, value);
+}
+
+bool GameStateManager::getWSCDidPlasmaDodge() {
+ return _WSCFlags.getFlag(kWSCDidPlasmaDodgeFlag);
+}
+
+void GameStateManager::setWSCSeenSinclairLecture(bool value) {
+ _WSCFlags.setFlag(kWSCSeenSinclairLectureFlag, value);
+}
+
+bool GameStateManager::getWSCSeenSinclairLecture() {
+ return _WSCFlags.getFlag(kWSCSeenSinclairLectureFlag);
+}
+
+void GameStateManager::setWSCBeenAtWSC93(bool value) {
+ _WSCFlags.setFlag(kWSCBeenAtWSC93Flag, value);
+}
+
+bool GameStateManager::getWSCBeenAtWSC93() {
+ return _WSCFlags.getFlag(kWSCBeenAtWSC93Flag);
+}
+
+void GameStateManager::setWSCCatwalkDark(bool value) {
+ _WSCFlags.setFlag(kWSCCatwalkDarkFlag, value);
+}
+
+bool GameStateManager::getWSCCatwalkDark() {
+ return _WSCFlags.getFlag(kWSCCatwalkDarkFlag);
+}
+
+void GameStateManager::setWSCRobotDead(bool value) {
+ _WSCFlags.setFlag(kWSCRobotDeadFlag, value);
+}
+
+bool GameStateManager::getWSCRobotDead() {
+ return _WSCFlags.getFlag(kWSCRobotDeadFlag);
+}
+
+void GameStateManager::setWSCRobotGone(bool value) {
+ _WSCFlags.setFlag(kWSCRobotGoneFlag, value);
+}
+
+bool GameStateManager::getWSCRobotGone() {
+ return _WSCFlags.getFlag(kWSCRobotGoneFlag);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/gamestate.h b/engines/pegasus/gamestate.h
new file mode 100644
index 0000000000..dd47bd6e51
--- /dev/null
+++ b/engines/pegasus/gamestate.h
@@ -0,0 +1,886 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_GAMESTATE_H
+#define PEGASUS_GAMESTATE_H
+
+#include "common/singleton.h"
+#include "common/util.h"
+
+#include "pegasus/types.h"
+#include "pegasus/util.h"
+#include "pegasus/items/item.h"
+
+namespace Common {
+ class Error;
+ class ReadStream;
+ class WriteStream;
+}
+
+namespace Pegasus {
+
+// The only things saved in here are things which get written out to a saved game file...
+
+enum {
+ kGlobalWalkthroughFlag,
+ kGlobalShieldOnFlag,
+ kGlobalEasterEggFlag,
+ kGlobalBeenToWSCFlag,
+ kGlobalBeenToMarsFlag,
+ kGlobalBeenToNoradFlag,
+ kGlobalWSCFinishedFlag,
+ kGlobalMarsFinishedFlag,
+ kGlobalNoradFinishedFlag,
+ kNumGlobalFlags
+};
+
+enum {
+ kScoringSawINNFlag,
+ kScoringTookShowerFlag,
+ kScoringFixedHairFlag,
+ kScoringGotKeyCardFlag,
+ kScoringReadPaperFlag,
+ kScoringLookThroughTelescopeFlag,
+ kScoringSawCaldoriaKioskFlag,
+ kScoringGoToTSAFlag,
+ kScoringEnterTSAFlag,
+ kScoringSawBust1Flag,
+ kScoringSawBust2Flag,
+ kScoringSawBust3Flag,
+ kScoringSawBust4Flag,
+ kScoringSawBust5Flag,
+ kScoringSawBust6Flag,
+ kScoringSawTheoryFlag,
+ kScoringSawBackgroundFlag,
+ kScoringSawProcedureFlag,
+ kScoringGotJourneymanKeyFlag,
+ kScoringGotPegasusBiochipFlag,
+ kScoringGotBiosuitFlag,
+ kScoringGoToPrehistoricFlag,
+ kScoringPutLogInReaderFlag,
+ kScoringSawCaldoriaNormalFlag,
+ kScoringSawCaldoriaAlteredFlag,
+ kScoringSawNoradNormalFlag,
+ kScoringSawNoradAlteredFlag,
+ kScoringSawMarsNormalFlag,
+ kScoringSawMarsAlteredFlag,
+ kScoringSawWSCNormalFlag,
+ kScoringSawWSCAlteredFlag,
+ kScoringWentToReadyRoom2Flag,
+ kScoringWentAfterSinclairFlag,
+ kScoringUsedCardBombFlag,
+ kScoringShieldedCardBombFlag,
+ kScoringStunnedSinclairFlag,
+ kScoringDisarmedNukeFlag,
+
+ kScoringThrewBreakerFlag,
+ kScoringExtendedBridgeFlag,
+ kScoringGotHistoricalLogFlag,
+ kScoringFinishedPrehistoricFlag,
+
+ kScoringThrownByRobotFlag,
+ kScoringGotMarsCardFlag,
+ kScoringSawMarsKioskFlag,
+ kScoringSawTransportMapFlag,
+ kScoringGotCrowBarFlag,
+ kScoringTurnedOnTransportFlag,
+ kScoringGotOxygenMaskFlag,
+ kScoringAvoidedRobotFlag,
+ kScoringActivatedPlatformFlag,
+ kScoringUsedLiquidNitrogenFlag,
+ kScoringUsedCrowBarFlag,
+ kScoringFoundCardBombFlag,
+ kScoringDisarmedCardBombFlag,
+ kScoringGotCardBombFlag,
+ kScoringThreadedMazeFlag,
+ kScoringThreadedGearRoomFlag,
+ kScoringEnteredShuttleFlag,
+ kScoringEnteredLaunchTubeFlag,
+ kScoringStoppedRobotsShuttleFlag,
+ kScoringGotMarsOpMemChipFlag,
+ kScoringFinishedMarsFlag,
+
+ kScoringSawSecurityMonitorFlag,
+ kScoringFilledOxygenCanisterFlag,
+ kScoringFilledArgonCanisterFlag,
+ kScoringSawUnconsciousOperatorFlag,
+ kScoringWentThroughPressureDoorFlag,
+ kScoringPreppedSubFlag,
+ kScoringEnteredSubFlag,
+ kScoringExitedSubFlag,
+ kScoringSawRobotAt54NorthFlag,
+ kScoringPlayedWithClawFlag,
+ kScoringUsedRetinalChipFlag,
+ kScoringFinishedGlobeGameFlag,
+ kScoringStoppedNoradRobotFlag,
+ kScoringGotNoradOpMemChipFlag,
+ kScoringFinishedNoradFlag,
+
+ kScoringRemovedDartFlag,
+ kScoringAnalyzedDartFlag,
+ kScoringBuiltAntidoteFlag,
+ kScoringGotSinclairKeyFlag,
+ kScoringGotArgonCanisterFlag,
+ kScoringGotNitrogenCanisterFlag,
+ kScoringPlayedWithMessagesFlag,
+ kScoringSawMorphExperimentFlag,
+ kScoringEnteredSinclairOfficeFlag,
+ kScoringSawBrochureFlag,
+ kScoringSawSinclairEntry1Flag,
+ kScoringSawSinclairEntry2Flag,
+ kScoringSawSinclairEntry3Flag,
+ kScoringSawWSCDirectoryFlag,
+ kScoringUsedCrowBarInWSCFlag,
+ kScoringFinishedPlasmaDodgeFlag,
+ kScoringOpenedCatwalkFlag,
+ kScoringStoppedWSCRobotFlag,
+ kScoringGotWSCOpMemChipFlag,
+ kScoringFinishedWSCFlag,
+
+ kScoringMarsGandhiFlag,
+ kScoringNoradGandhiFlag,
+ kScoringWSCGandhiFlag,
+
+ kNumScoringFlags
+};
+
+enum {
+ kCaldoriaSeenPullbackFlag,
+ kCaldoriaMadeOJFlag,
+ kCaldoriaWokenUpFlag,
+ kCaldoriaDidRecalibrationFlag,
+ kCaldoriaSeenSinclairInElevatorFlag,
+ kCaldoriaINNAnnouncingFlag,
+ kCaldoriaSeenINNFlag,
+ kCaldoriaSeenMessagesFlag,
+ kCaldoriaSinclairShotFlag,
+ kCaldoriaBombDisarmedFlag,
+ kCaldoriaRoofDoorOpenFlag,
+ kCaldoriaDoneHygieneFlag,
+ kCaldoriaSawVoiceAnalysisFlag,
+ kCaldoriaDoorBombedFlag,
+ kCaldoriaGunAimedFlag,
+ kNumCaldoriaFlags
+};
+
+enum {
+ kCaldoriaNoFuseRunning,
+ kCaldoriaDoorBombFuseRunning,
+ kCaldoriaSinclairFuseRunning
+};
+
+enum {
+ kTSAIDedAtDoorFlag,
+ kTSA0BZoomedInFlag,
+ kTSAFrontDoorUnlockedOutsideFlag,
+ kTSAFrontDoorUnlockedInsideFlag,
+ kTSASeenRobotGreetingFlag,
+ kTSASeenTheoryFlag,
+ kTSASeenBackgroundFlag,
+ kTSASeenProcedureFlag,
+ kTSASeenAgent3AtDoorFlag,
+ kTSACommandCenterLockedFlag,
+ kTSASeenCaldoriaNormalFlag,
+ kTSASeenCaldoriaAlteredFlag,
+ kTSASeenNoradNormalFlag,
+ kTSASeenNoradAlteredFlag,
+ kTSASeenMarsNormalFlag,
+ kTSASeenMarsAlteredFlag,
+ kTSASeenWSCNormalFlag,
+ kTSASeenWSCAlteredFlag,
+ kTSABiosuitOnFlag,
+ kNumTSAFlags
+};
+
+enum {
+ kPrehistoricTriedToExtendBridgeFlag,
+ kPrehistoricSeenTimeStreamFlag,
+ kPrehistoricSeenFlyer1Flag,
+ kPrehistoricSeenFlyer2Flag,
+ kPrehistoricSeenBridgeZoomFlag,
+ kPrehistoricBreakerThrownFlag,
+ kNumPrehistoricFlags
+};
+
+enum {
+ kNoradSeenTimeStreamFlag,
+ kNoradGassedFlag,
+ kNoradFillingStationOnFlag,
+ kNoradN22MessagePlayedFlag,
+ kNoradArrivedFromSubFlag,
+ kNoradWaitingForLaserFlag,
+ kNoradRetScanGoodFlag,
+ kNoradPlayedGlobeGameFlag,
+ kNoradBeatRobotWithClawFlag,
+ kNoradBeatRobotWithDoorFlag,
+ kNumNoradFlags
+};
+
+enum {
+ kMarsSeenTimeStreamFlag,
+ kMarsHeardUpperPodMessageFlag,
+ kMarsRobotThrownPlayerFlag,
+ kMarsHeardCheckInMessageFlag,
+ kMarsPodAtUpperPlatformFlag,
+ kMarsSeenThermalScanFlag,
+ kMarsArrivedBelowFlag,
+ kMarsSeenRobotAtReactorFlag,
+ kMarsAvoidedReactorRobotFlag,
+ kMarsInAirlockFlag,
+ kMarsAirlockOpenFlag,
+ kMarsMaskOnFillerFlag,
+ kMarsLockFrozenFlag,
+ kMarsLockBrokenFlag,
+ kMarsMazeDoorPair1Flag,
+ kMarsMazeDoorPair2Flag,
+ kMarsMazeDoorPair3Flag,
+ kMarsSawRobotLeaveFlag,
+ kMarsSecurityDownFlag,
+ kMarsHitRobotWithCannonFlag,
+ kMarsReadyForShuttleTransportFlag,
+ kMarsFinishedCanyonChaseFlag,
+ kMarsThreadedMazeFlag,
+ kNumMarsFlags
+};
+
+enum {
+ kWSCSeenTimeStreamFlag,
+ kWSCPoisonedFlag,
+ kWSCAnsweredAboutDartFlag,
+ kWSCRemovedDartFlag,
+ kWSCAnalyzerOnFlag,
+ kWSCDartInAnalyzerFlag,
+ kWSCAnalyzedDartFlag,
+ kWSCSawMorphFlag,
+ kWSCDesignedAntidoteFlag,
+ kWSCPickedUpAntidoteFlag,
+ kWSCOfficeMessagesOpenFlag,
+ kWSCSeenNerdFlag,
+ kWSCHeardPage1Flag,
+ kWSCHeardPage2Flag,
+ kWSCHeardCheckInFlag,
+ kWSCDidPlasmaDodgeFlag,
+ kWSCSeenSinclairLectureFlag,
+ kWSCBeenAtWSC93Flag,
+ kWSCCatwalkDarkFlag,
+ kWSCRobotDeadFlag,
+ kWSCRobotGoneFlag,
+ kNumWSCFlags
+};
+
+class GameStateManager : public Common::Singleton<GameStateManager> {
+public:
+ GameStateManager() { resetGameState(); }
+
+ // Base game state
+ Common::Error writeGameState(Common::WriteStream *stream);
+ Common::Error readGameState(Common::ReadStream *stream);
+
+ void resetGameState();
+
+ void getCurrentLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction);
+ void setCurrentLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction);
+
+ NeighborhoodID getCurrentNeighborhood();
+ void setCurrentNeighborhood(const NeighborhoodID neighborhood);
+ RoomID getCurrentRoom();
+ void setCurrentRoom(const RoomID room);
+ DirectionConstant getCurrentDirection();
+ void setCurrentDirection(const DirectionConstant direction);
+
+ RoomViewID getCurrentRoomAndView();
+
+ void getNextLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction);
+ void setNextLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction);
+
+ NeighborhoodID getNextNeighborhood();
+ void setNextNeighborhood(const NeighborhoodID neighborhood);
+ RoomID getNextRoom();
+ void setNextRoom(const RoomID room);
+ DirectionConstant getNextDirection();
+ void setNextDirection(const DirectionConstant direction);
+
+ void getLastLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction);
+ void setLastLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction);
+
+ NeighborhoodID getLastNeighborhood();
+ void setLastNeighborhood(const NeighborhoodID neighborhood);
+ RoomID getLastRoom();
+ void setLastRoom(const RoomID room);
+ DirectionConstant getLastDirection();
+ void setLastDirection(const DirectionConstant direction);
+
+ RoomViewID getLastRoomAndView();
+
+ void getOpenDoorLocation(RoomID &room, DirectionConstant &direction);
+ void setOpenDoorLocation(const RoomID room, const DirectionConstant direction);
+ RoomID getOpenDoorRoom();
+ void setOpenDoorRoom(const RoomID room);
+ DirectionConstant getOpenDoorDirection();
+ void setOpenDoorDirection(const DirectionConstant direction);
+
+ RoomViewID getDoorOpenRoomAndView();
+
+ bool isCurrentDoorOpen();
+
+ // Pegasus Prime
+
+ // Scoring...
+ // Scoring "Set" functions.
+ // Caldoria/TSA scoring
+ void setScoringSawINN(const bool = true);
+ void setScoringTookShower(const bool = true);
+ void setScoringFixedHair(const bool = true);
+ void setScoringGotKeyCard(const bool = true);
+ void setScoringReadPaper(const bool = true);
+ void setScoringLookThroughTelescope(const bool = true);
+ void setScoringSawCaldoriaKiosk(const bool = true);
+ void setScoringGoToTSA(const bool = true);
+ void setScoringEnterTSA(const bool = true);
+ void setScoringSawBust1(const bool = true);
+ void setScoringSawBust2(const bool = true);
+ void setScoringSawBust3(const bool = true);
+ void setScoringSawBust4(const bool = true);
+ void setScoringSawBust5(const bool = true);
+ void setScoringSawBust6(const bool = true);
+ void setScoringSawTheory(const bool = true);
+ void setScoringSawBackground(const bool = true);
+ void setScoringSawProcedure(const bool = true);
+ void setScoringGotJourneymanKey(const bool = true);
+ void setScoringGotPegasusBiochip(const bool = true);
+ void setScoringGotBiosuit(const bool = true);
+ void setScoringGoToPrehistoric(const bool = true);
+ void setScoringPutLogInReader(const bool = true);
+ void setScoringSawCaldoriaNormal(const bool = true);
+ void setScoringSawCaldoriaAltered(const bool = true);
+ void setScoringSawNoradNormal(const bool = true);
+ void setScoringSawNoradAltered(const bool = true);
+ void setScoringSawMarsNormal(const bool = true);
+ void setScoringSawMarsAltered(const bool = true);
+ void setScoringSawWSCNormal(const bool = true);
+ void setScoringSawWSCAltered(const bool = true);
+ void setScoringWentToReadyRoom2(const bool = true);
+ void setScoringWentAfterSinclair(const bool = true);
+ void setScoringUsedCardBomb(const bool = true);
+ void setScoringShieldedCardBomb(const bool = true);
+ void setScoringStunnedSinclair(const bool = true);
+ void setScoringDisarmedNuke(const bool = true);
+
+ // Prehistoric scoring
+ void setScoringThrewBreaker(const bool = true);
+ void setScoringExtendedBridge(const bool = true);
+ void setScoringGotHistoricalLog(const bool = true);
+ void setScoringFinishedPrehistoric(const bool = true);
+
+ // Mars scoring
+ void setScoringThrownByRobot(const bool = true);
+ void setScoringGotMarsCard(const bool = true);
+ void setScoringSawMarsKiosk(const bool = true);
+ void setScoringSawTransportMap(const bool = true);
+ void setScoringGotCrowBar(const bool = true);
+ void setScoringTurnedOnTransport(const bool = true);
+ void setScoringGotOxygenMask(const bool = true);
+ void setScoringAvoidedRobot(const bool = true);
+ void setScoringActivatedPlatform(const bool = true);
+ void setScoringUsedLiquidNitrogen(const bool = true);
+ void setScoringUsedCrowBar(const bool = true);
+ void setScoringFoundCardBomb(const bool = true);
+ void setScoringDisarmedCardBomb(const bool = true);
+ void setScoringGotCardBomb(const bool = true);
+ void setScoringThreadedMaze(const bool = true);
+ void setScoringThreadedGearRoom(const bool = true);
+ void setScoringEnteredShuttle(const bool = true);
+ void setScoringEnteredLaunchTube(const bool = true);
+ void setScoringStoppedRobotsShuttle(const bool = true);
+ void setScoringGotMarsOpMemChip(const bool = true);
+ void setScoringFinishedMars(const bool = true);
+
+ // Norad scoring
+ void setScoringSawSecurityMonitor(const bool = true);
+ void setScoringFilledOxygenCanister(const bool = true);
+ void setScoringFilledArgonCanister(const bool = true);
+ void setScoringSawUnconsciousOperator(const bool = true);
+ void setScoringWentThroughPressureDoor(const bool = true);
+ void setScoringPreppedSub(const bool = true);
+ void setScoringEnteredSub(const bool = true);
+ void setScoringExitedSub(const bool = true);
+ void setScoringSawRobotAt54North(const bool = true);
+ void setScoringPlayedWithClaw(const bool = true);
+ void setScoringUsedRetinalChip(const bool = true);
+ void setScoringFinishedGlobeGame(const bool = true);
+ void setScoringStoppedNoradRobot(const bool = true);
+ void setScoringGotNoradOpMemChip(const bool = true);
+ void setScoringFinishedNorad(const bool = true);
+
+ // WSC scoring
+ void setScoringRemovedDart(const bool = true);
+ void setScoringAnalyzedDart(const bool = true);
+ void setScoringBuiltAntidote(const bool = true);
+ void setScoringGotSinclairKey(const bool = true);
+ void setScoringGotArgonCanister(const bool = true);
+ void setScoringGotNitrogenCanister(const bool = true);
+ void setScoringPlayedWithMessages(const bool = true);
+ void setScoringSawMorphExperiment(const bool = true);
+ void setScoringEnteredSinclairOffice(const bool = true);
+ void setScoringSawBrochure(const bool = true);
+ void setScoringSawSinclairEntry1(const bool = true);
+ void setScoringSawSinclairEntry2(const bool = true);
+ void setScoringSawSinclairEntry3(const bool = true);
+ void setScoringSawWSCDirectory(const bool = true);
+ void setScoringUsedCrowBarInWSC(const bool = true);
+ void setScoringFinishedPlasmaDodge(const bool = true);
+ void setScoringOpenedCatwalk(const bool = true);
+ void setScoringStoppedWSCRobot(const bool = true);
+ void setScoringGotWSCOpMemChip(const bool = true);
+ void setScoringFinishedWSC(const bool = true);
+
+ // Gandhi scoring
+ void setScoringMarsGandhi(const bool = true);
+ void setScoringNoradGandhi(const bool = true);
+ void setScoringWSCGandhi(const bool = true);
+
+ // Scoring "Get" functions.
+ bool getScoringSawINN();
+ bool getScoringTookShower();
+ bool getScoringFixedHair();
+ bool getScoringGotKeyCard();
+ bool getScoringReadPaper();
+ bool getScoringLookThroughTelescope();
+ bool getScoringSawCaldoriaKiosk();
+ bool getScoringGoToTSA();
+ bool getScoringEnterTSA();
+ bool getScoringSawBust1();
+ bool getScoringSawBust2();
+ bool getScoringSawBust3();
+ bool getScoringSawBust4();
+ bool getScoringSawBust5();
+ bool getScoringSawBust6();
+ bool getScoringSawTheory();
+ bool getScoringSawBackground();
+ bool getScoringSawProcedure();
+ bool getScoringGotJourneymanKey();
+ bool getScoringGotPegasusBiochip();
+ bool getScoringGotBiosuit();
+ bool getScoringGoToPrehistoric();
+ bool getScoringPutLogInReader();
+ bool getScoringSawCaldoriaNormal();
+ bool getScoringSawCaldoriaAltered();
+ bool getScoringSawNoradNormal();
+ bool getScoringSawNoradAltered();
+ bool getScoringSawMarsNormal();
+ bool getScoringSawMarsAltered();
+ bool getScoringSawWSCNormal();
+ bool getScoringSawWSCAltered();
+ bool getScoringWentToReadyRoom2();
+ bool getScoringWentAfterSinclair();
+ bool getScoringUsedCardBomb();
+ bool getScoringShieldedCardBomb();
+ bool getScoringStunnedSinclair();
+ bool getScoringDisarmedNuke();
+ bool getScoringThrewBreaker();
+ bool getScoringExtendedBridge();
+ bool getScoringGotHistoricalLog();
+ bool getScoringFinishedPrehistoric();
+ bool getScoringThrownByRobot();
+ bool getScoringGotMarsCard();
+ bool getScoringSawMarsKiosk();
+ bool getScoringSawTransportMap();
+ bool getScoringGotCrowBar();
+ bool getScoringTurnedOnTransport();
+ bool getScoringGotOxygenMask();
+ bool getScoringAvoidedRobot();
+ bool getScoringActivatedPlatform();
+ bool getScoringUsedLiquidNitrogen();
+ bool getScoringUsedCrowBar();
+ bool getScoringFoundCardBomb();
+ bool getScoringDisarmedCardBomb();
+ bool getScoringGotCardBomb();
+ bool getScoringThreadedMaze();
+ bool getScoringThreadedGearRoom();
+ bool getScoringEnteredShuttle();
+ bool getScoringEnteredLaunchTube();
+ bool getScoringStoppedRobotsShuttle();
+ bool getScoringGotMarsOpMemChip();
+ bool getScoringFinishedMars();
+ bool getScoringSawSecurityMonitor();
+ bool getScoringFilledOxygenCanister();
+ bool getScoringFilledArgonCanister();
+ bool getScoringSawUnconsciousOperator();
+ bool getScoringWentThroughPressureDoor();
+ bool getScoringPreppedSub();
+ bool getScoringEnteredSub();
+ bool getScoringExitedSub();
+ bool getScoringSawRobotAt54North();
+ bool getScoringPlayedWithClaw();
+ bool getScoringUsedRetinalChip();
+ bool getScoringFinishedGlobeGame();
+ bool getScoringStoppedNoradRobot();
+ bool getScoringGotNoradOpMemChip();
+ bool getScoringFinishedNorad();
+ bool getScoringRemovedDart();
+ bool getScoringAnalyzedDart();
+ bool getScoringBuiltAntidote();
+ bool getScoringGotSinclairKey();
+ bool getScoringGotArgonCanister();
+ bool getScoringGotNitrogenCanister();
+ bool getScoringPlayedWithMessages();
+ bool getScoringSawMorphExperiment();
+ bool getScoringEnteredSinclairOffice();
+ bool getScoringSawBrochure();
+ bool getScoringSawSinclairEntry1();
+ bool getScoringSawSinclairEntry2();
+ bool getScoringSawSinclairEntry3();
+ bool getScoringSawWSCDirectory();
+ bool getScoringUsedCrowBarInWSC();
+ bool getScoringFinishedPlasmaDodge();
+ bool getScoringOpenedCatwalk();
+ bool getScoringStoppedWSCRobot();
+ bool getScoringGotWSCOpMemChip();
+ bool getScoringFinishedWSC();
+ bool getScoringMarsGandhi();
+ bool getScoringNoradGandhi();
+ bool getScoringWSCGandhi();
+
+ GameScoreType getCaldoriaTSAScore();
+ GameScoreType getPrehistoricScore();
+ GameScoreType getMarsScore();
+ GameScoreType getNoradScore();
+ GameScoreType getWSCScore();
+ GameScoreType getGandhiScore();
+ GameScoreType getTotalScore();
+
+ void writeCaldoriaState(Common::WriteStream *stream);
+ void readCaldoriaState(Common::ReadStream *stream);
+ void resetCaldoriaState();
+
+ void writeTSAState(Common::WriteStream *stream);
+ void readTSAState(Common::ReadStream *stream);
+ void resetTSAState();
+
+ void writePrehistoricState(Common::WriteStream *stream);
+ void readPrehistoricState(Common::ReadStream *stream);
+ void resetPrehistoricState();
+
+ void writeNoradState(Common::WriteStream *stream);
+ void readNoradState(Common::ReadStream *stream);
+ void resetNoradState();
+
+ void writeMarsState(Common::WriteStream *stream);
+ void readMarsState(Common::ReadStream *stream);
+ void resetMarsState();
+
+ void writeWSCState(Common::WriteStream *stream);
+ void readWSCState(Common::ReadStream *stream);
+ void resetWSCState();
+
+ // Globals.
+ void setWalkthroughMode(bool);
+ bool getWalkthroughMode();
+ void setShieldOn(bool);
+ bool getShieldOn();
+ void setEasterEgg(bool);
+ bool getEasterEgg();
+ void setBeenToWSC(bool value);
+ bool getBeenToWSC();
+ void setBeenToMars(bool value);
+ bool getBeenToMars();
+ void setBeenToNorad(bool value);
+ bool getBeenToNorad();
+ void setWSCFinished(bool);
+ bool getWSCFinished();
+ void setMarsFinished(bool);
+ bool getMarsFinished();
+ void setNoradFinished(bool);
+ bool getNoradFinished();
+ bool allTimeZonesFinished();
+ void setTakenItemID(ItemID, bool);
+ bool isTakenItemID(ItemID);
+ void setTakenItem(Item *, bool);
+ bool isTakenItem(Item *);
+
+ // Caldoria
+ void setCaldoriaFuseTimeLimit(const TimeValue);
+ TimeValue getCaldoriaFuseTimeLimit();
+ void setCaldoriaSeenPullback(bool);
+ bool getCaldoriaSeenPullback();
+ void setCaldoriaMadeOJ(bool);
+ bool getCaldoriaMadeOJ();
+ void setCaldoriaWokenUp(bool);
+ bool getCaldoriaWokenUp();
+ void setCaldoriaDidRecalibration(bool);
+ bool getCaldoriaDidRecalibration();
+ void setCaldoriaSeenSinclairInElevator(bool);
+ bool getCaldoriaSeenSinclairInElevator();
+ void setCaldoriaINNAnnouncing(bool);
+ bool getCaldoriaINNAnnouncing();
+ void setCaldoriaSeenINN(bool);
+ bool getCaldoriaSeenINN();
+ void setCaldoriaSeenMessages(bool);
+ bool getCaldoriaSeenMessages();
+ void setCaldoriaSinclairShot(bool);
+ bool getCaldoriaSinclairShot();
+ void setCaldoriaBombDisarmed(bool);
+ bool getCaldoriaBombDisarmed();
+ void setCaldoriaRoofDoorOpen(bool);
+ bool getCaldoriaRoofDoorOpen();
+ void setCaldoriaDoneHygiene(bool);
+ bool getCaldoriaDoneHygiene();
+ void setCaldoriaSawVoiceAnalysis(bool);
+ bool getCaldoriaSawVoiceAnalysis();
+ void setCaldoriaDoorBombed(bool);
+ bool getCaldoriaDoorBombed();
+ void setCaldoriaGunAimed(bool);
+ bool getCaldoriaGunAimed();
+
+ // TSA
+ void setRipTimerTime(TimeValue);
+ TimeValue getRipTimerTime();
+ void setTSAFuseTimeLimit(TimeValue);
+ TimeValue getTSAFuseTimeLimit();
+ void setT0BMonitorMode(byte);
+ byte getT0BMonitorMode();
+ void setTSAState(byte);
+ byte getTSAState();
+ void setT0BMonitorStart(TimeValue);
+ TimeValue getT0BMonitorStart();
+ void setTSAIDedAtDoor(bool);
+ bool getTSAIDedAtDoor();
+ void setTSA0BZoomedIn(bool);
+ bool getTSA0BZoomedIn();
+ void setTSAFrontDoorUnlockedOutside(bool);
+ bool getTSAFrontDoorUnlockedOutside();
+ void setTSAFrontDoorUnlockedInside(bool);
+ bool getTSAFrontDoorUnlockedInside();
+ void setTSASeenRobotGreeting(bool);
+ bool getTSASeenRobotGreeting();
+ void setTSASeenTheory(bool);
+ bool getTSASeenTheory();
+ void setTSASeenBackground(bool);
+ bool getTSASeenBackground();
+ void setTSASeenProcedure(bool);
+ bool getTSASeenProcedure();
+ void setTSASeenAgent3AtDoor(bool);
+ bool getTSASeenAgent3AtDoor();
+ void setTSACommandCenterLocked(bool);
+ bool getTSACommandCenterLocked();
+ void setTSASeenCaldoriaNormal(bool);
+ bool getTSASeenCaldoriaNormal();
+ void setTSASeenCaldoriaAltered(bool);
+ bool getTSASeenCaldoriaAltered();
+ void setTSASeenNoradNormal(bool);
+ bool getTSASeenNoradNormal();
+ void setTSASeenNoradAltered(bool);
+ bool getTSASeenNoradAltered();
+ void setTSASeenMarsNormal(bool);
+ bool getTSASeenMarsNormal();
+ void setTSASeenMarsAltered(bool);
+ bool getTSASeenMarsAltered();
+ void setTSASeenWSCNormal(bool);
+ bool getTSASeenWSCNormal();
+ void setTSASeenWSCAltered(bool);
+ bool getTSASeenWSCAltered();
+ void setTSABiosuitOn(bool);
+ bool getTSABiosuitOn();
+
+ // Prehistoric
+ void setPrehistoricTriedToExtendBridge(bool);
+ bool getPrehistoricTriedToExtendBridge();
+ void setPrehistoricSeenTimeStream(bool);
+ bool getPrehistoricSeenTimeStream();
+ void setPrehistoricSeenFlyer1(bool);
+ bool getPrehistoricSeenFlyer1();
+ void setPrehistoricSeenFlyer2(bool);
+ bool getPrehistoricSeenFlyer2();
+ void setPrehistoricSeenBridgeZoom(bool);
+ bool getPrehistoricSeenBridgeZoom();
+ void setPrehistoricBreakerThrown(bool);
+ bool getPrehistoricBreakerThrown();
+
+ // Norad
+ void setNoradSeenTimeStream(bool);
+ bool getNoradSeenTimeStream();
+ void setNoradGassed(bool);
+ bool getNoradGassed();
+ void setNoradFillingStationOn(bool);
+ bool getNoradFillingStationOn();
+ void setNoradN22MessagePlayed(bool);
+ bool getNoradN22MessagePlayed();
+ void setNoradPlayedGlobeGame(bool);
+ bool getNoradPlayedGlobeGame();
+ void setNoradBeatRobotWithClaw(bool);
+ bool getNoradBeatRobotWithClaw();
+ void setNoradBeatRobotWithDoor(bool);
+ bool getNoradBeatRobotWithDoor();
+ void setNoradRetScanGood(bool);
+ bool getNoradRetScanGood();
+ void setNoradWaitingForLaser(bool);
+ bool getNoradWaitingForLaser();
+ void setNoradSubRoomPressure(uint16);
+ uint16 getNoradSubRoomPressure();
+ void setNoradSubPrepState(NoradSubPrepState);
+ NoradSubPrepState getNoradSubPrepState();
+ void setNoradArrivedFromSub(bool);
+ bool getNoradArrivedFromSub();
+
+ // Mars
+ void setMarsSeenTimeStream(bool);
+ bool getMarsSeenTimeStream();
+ void setMarsHeardUpperPodMessage(bool);
+ bool getMarsHeardUpperPodMessage();
+ void setMarsRobotThrownPlayer(bool);
+ bool getMarsRobotThrownPlayer();
+ void setMarsHeardCheckInMessage(bool);
+ bool getMarsHeardCheckInMessage();
+ void setMarsPodAtUpperPlatform(bool);
+ bool getMarsPodAtUpperPlatform();
+ void setMarsSeenThermalScan(bool);
+ bool getMarsSeenThermalScan();
+ void setMarsArrivedBelow(bool);
+ bool getMarsArrivedBelow();
+ void setMarsSeenRobotAtReactor(bool);
+ bool getMarsSeenRobotAtReactor();
+ void setMarsAvoidedReactorRobot(bool);
+ bool getMarsAvoidedReactorRobot();
+ void setMarsInAirlock(bool);
+ bool getMarsInAirlock();
+ void setMarsAirlockOpen(bool);
+ bool getMarsAirlockOpen();
+ void setMarsMaskOnFiller(bool);
+ bool getMarsMaskOnFiller();
+ void setMarsLockFrozen(bool);
+ bool getMarsLockFrozen();
+ void setMarsLockBroken(bool);
+ bool getMarsLockBroken();
+ void setMarsMazeDoorPair1(bool);
+ bool getMarsMazeDoorPair1();
+ void setMarsMazeDoorPair2(bool);
+ bool getMarsMazeDoorPair2();
+ void setMarsMazeDoorPair3(bool);
+ bool getMarsMazeDoorPair3();
+ void setMarsSawRobotLeave(bool);
+ bool getMarsSawRobotLeave();
+ void setMarsSecurityDown(bool);
+ bool getMarsSecurityDown();
+ void setMarsFinishedCanyonChase(bool);
+ bool getMarsFinishedCanyonChase();
+ void setMarsThreadedMaze(bool);
+ bool getMarsThreadedMaze();
+ void setMarsHitRobotWithCannon(bool);
+ bool getMarsHitRobotWithCannon();
+ void setMarsReadyForShuttleTransport(bool);
+ bool getMarsReadyForShuttleTransport();
+
+ // WSC
+ void setWSCSeenTimeStream(bool);
+ bool getWSCSeenTimeStream();
+ void setWSCPoisoned(bool);
+ bool getWSCPoisoned();
+ void setWSCAnsweredAboutDart(bool);
+ bool getWSCAnsweredAboutDart();
+ void setWSCRemovedDart(bool);
+ bool getWSCRemovedDart();
+ void setWSCAnalyzerOn(bool);
+ bool getWSCAnalyzerOn();
+ void setWSCDartInAnalyzer(bool);
+ bool getWSCDartInAnalyzer();
+ void setWSCAnalyzedDart(bool);
+ bool getWSCAnalyzedDart();
+ void setWSCSawMorph(bool);
+ bool getWSCSawMorph();
+ void setWSCDesignedAntidote(bool);
+ bool getWSCDesignedAntidote();
+ void setWSCPickedUpAntidote(bool);
+ bool getWSCPickedUpAntidote();
+ void setWSCOfficeMessagesOpen(bool);
+ bool getWSCOfficeMessagesOpen();
+ void setWSCSeenNerd(bool);
+ bool getWSCSeenNerd();
+ void setWSCHeardPage1(bool);
+ bool getWSCHeardPage1();
+ void setWSCHeardPage2(bool);
+ bool getWSCHeardPage2();
+ void setWSCHeardCheckIn(bool);
+ bool getWSCHeardCheckIn();
+ void setWSCDidPlasmaDodge(bool);
+ bool getWSCDidPlasmaDodge();
+ void setWSCSeenSinclairLecture(bool);
+ bool getWSCSeenSinclairLecture();
+ void setWSCBeenAtWSC93(bool);
+ bool getWSCBeenAtWSC93();
+ void setWSCCatwalkDark(bool);
+ bool getWSCCatwalkDark();
+ void setWSCRobotDead(bool);
+ bool getWSCRobotDead();
+ void setWSCRobotGone(bool);
+ bool getWSCRobotGone();
+
+protected:
+ friend class Common::Singleton<SingletonBaseType>;
+
+private:
+ // Base
+ NeighborhoodID _currentNeighborhood;
+ RoomID _currentRoom;
+ DirectionConstant _currentDirection;
+ NeighborhoodID _nexNeighborhoodID;
+ RoomID _nextRoomID;
+ DirectionConstant _nextDirection;
+ NeighborhoodID _lastNeighborhood;
+ RoomID _lastRoom;
+ DirectionConstant _lastDirection;
+ RoomID _openDoorRoom;
+ DirectionConstant _openDoorDirection;
+
+ // Pegasus Prime
+ FlagsArray<byte, kNumGlobalFlags> _globalFlags;
+ FlagsArray<byte, kNumScoringFlags> _scoringFlags;
+ FlagsArray<uint32, kNumItems> _itemTakenFlags;
+
+ FlagsArray<byte, kNumCaldoriaFlags> _caldoriaFlags;
+ TimeValue _caldoriaFuseTimeLimit;
+
+ TimeValue _TSARipTimerTime;
+ TimeValue _TSAFuseTimeLimit;
+ byte _TSAState;
+ byte _T0BMonitorMode;
+ TimeValue _T0BMonitorStart;
+ FlagsArray<byte, kNumTSAFlags> _TSAFlags;
+
+ FlagsArray<byte, kNumPrehistoricFlags> _prehistoricFlags;
+
+ FlagsArray<byte, kNumNoradFlags> _noradFlags;
+ uint16 _noradSubRoomPressure;
+ NoradSubPrepState _noradSubPrepState;
+
+ FlagsArray<byte, kNumMarsFlags> _marsFlags;
+
+ FlagsArray<byte, kNumWSCFlags> _WSCFlags;
+};
+
+} // End of namespace Pegasus
+
+#define GameState (::Pegasus::GameStateManager::instance())
+
+#endif
diff --git a/engines/pegasus/graphics.cpp b/engines/pegasus/graphics.cpp
new file mode 100644
index 0000000000..8dbd678809
--- /dev/null
+++ b/engines/pegasus/graphics.cpp
@@ -0,0 +1,346 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/events.h"
+#include "common/file.h"
+#include "common/textconsole.h"
+#include "engines/util.h"
+
+#include "pegasus/elements.h"
+#include "pegasus/graphics.h"
+#include "pegasus/transition.h"
+
+namespace Pegasus {
+
+GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) {
+ initGraphics(640, 480, true, NULL);
+
+ if (_vm->_system->getScreenFormat().bytesPerPixel == 1)
+ error("No true color mode available");
+
+ _backLayer = kMinAvailableOrder;
+ _frontLayer = kMaxAvailableOrder;
+ _firstDisplayElement = _lastDisplayElement = 0;
+ _workArea.create(640, 480, _vm->_system->getScreenFormat());
+ _modifiedScreen = false;
+ _curSurface = &_workArea;
+ _erase = false;
+ _updatesEnabled = true;
+ _screenFader = new ScreenFader();
+}
+
+GraphicsManager::~GraphicsManager() {
+ _workArea.free();
+ delete _screenFader;
+}
+
+void GraphicsManager::invalRect(const Common::Rect &rect) {
+ // We're using a simpler algorithm for dirty rect handling than the original
+ // The original was way too overcomplicated for what we need here now.
+
+ if (_dirtyRect.width() == 0 || _dirtyRect.height() == 0) {
+ // We have no dirty rect, so this is now our dirty rect
+ _dirtyRect = rect;
+ } else {
+ // Expand our dirty rect to include rect
+ _dirtyRect.extend(rect);
+ }
+
+ // Sanity check: clip our rect to the screen
+ _dirtyRect.right = MIN<int>(640, _dirtyRect.right);
+ _dirtyRect.bottom = MIN<int>(480, _dirtyRect.bottom);
+}
+
+void GraphicsManager::addDisplayElement(DisplayElement *newElement) {
+ newElement->_elementOrder = CLIP<int>(newElement->_elementOrder, kMinAvailableOrder, kMaxAvailableOrder);
+
+ if (_firstDisplayElement) {
+ DisplayElement *runner = _firstDisplayElement;
+ DisplayElement *lastRunner = 0;
+
+ // Search for first element whose display order is greater than
+ // the new element's and add the new element just before it.
+ while (runner) {
+ if (newElement->_elementOrder < runner->_elementOrder) {
+ if (lastRunner) {
+ lastRunner->_nextElement = newElement;
+ newElement->_nextElement = runner;
+ } else {
+ newElement->_nextElement = _firstDisplayElement;
+ _firstDisplayElement = newElement;
+ }
+ break;
+ }
+ lastRunner = runner;
+ runner = runner->_nextElement;
+ }
+
+ // If got here and runner == NULL, we ran through the whole list without
+ // inserting, so add at the end.
+ if (!runner) {
+ _lastDisplayElement->_nextElement = newElement;
+ _lastDisplayElement = newElement;
+ }
+ } else {
+ _firstDisplayElement = newElement;
+ _lastDisplayElement = newElement;
+ }
+
+ newElement->_elementIsDisplaying = true;
+}
+
+void GraphicsManager::removeDisplayElement(DisplayElement *oldElement) {
+ if (!_firstDisplayElement)
+ return;
+
+ if (oldElement == _firstDisplayElement) {
+ if (oldElement == _lastDisplayElement) {
+ _firstDisplayElement = 0;
+ _lastDisplayElement = 0;
+ } else {
+ _firstDisplayElement = oldElement->_nextElement;
+ }
+
+ invalRect(oldElement->_bounds);
+ } else {
+ // Scan list for element.
+ // If we get here, we know that the list has at least one item, and it
+ // is not the first item, so we can skip it.
+ DisplayElement *runner = _firstDisplayElement->_nextElement;
+ DisplayElement *lastRunner = _firstDisplayElement;
+
+ while (runner) {
+ if (runner == oldElement) {
+ lastRunner->_nextElement = runner->_nextElement;
+
+ if (oldElement == _lastDisplayElement)
+ _lastDisplayElement = lastRunner;
+
+ invalRect(oldElement->_bounds);
+ break;
+ }
+
+ lastRunner = runner;
+ runner = runner->_nextElement;
+ }
+ }
+
+ oldElement->_nextElement = 0;
+ oldElement->_elementIsDisplaying = false;
+}
+
+void GraphicsManager::updateDisplay() {
+ bool screenDirty = false;
+
+ if (!_dirtyRect.isEmpty()) {
+ // Fill the dirty area with black if erase mode is enabled
+ if (_erase)
+ _workArea.fillRect(_dirtyRect, _workArea.format.RGBToColor(0, 0, 0));
+
+ for (DisplayElement *runner = _firstDisplayElement; runner != 0; runner = runner->_nextElement) {
+ Common::Rect bounds;
+ runner->getBounds(bounds);
+
+ // TODO: Better logic; it does a bit more work than it probably needs to
+ // but it should work fine for now.
+ if (bounds.intersects(_dirtyRect) && runner->validToDraw(_backLayer, _frontLayer)) {
+ runner->draw(bounds);
+ screenDirty = true;
+ }
+ }
+
+ // Copy only the dirty rect to the screen
+ if (screenDirty)
+ g_system->copyRectToScreen((byte *)_workArea.getBasePtr(_dirtyRect.left, _dirtyRect.top), _workArea.pitch, _dirtyRect.left, _dirtyRect.top, _dirtyRect.width(), _dirtyRect.height());
+
+ // Clear the dirty rect
+ _dirtyRect = Common::Rect();
+ }
+
+ if (_updatesEnabled && (screenDirty || _modifiedScreen))
+ g_system->updateScreen();
+
+ _modifiedScreen = false;
+}
+
+void GraphicsManager::clearScreen() {
+ Graphics::Surface *screen = g_system->lockScreen();
+ screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0));
+ g_system->unlockScreen();
+ _modifiedScreen = true;
+}
+
+DisplayElement *GraphicsManager::findDisplayElement(const DisplayElementID id) {
+ DisplayElement *runner = _firstDisplayElement;
+
+ while (runner) {
+ if (runner->getObjectID() == id)
+ return runner;
+ runner = runner->_nextElement;
+ }
+
+ return 0;
+}
+
+void GraphicsManager::doFadeOutSync(const TimeValue time, const TimeScale scale, bool isBlack) {
+ _updatesEnabled = false;
+ _screenFader->doFadeOutSync(time, scale, isBlack);
+}
+
+void GraphicsManager::doFadeInSync(const TimeValue time, const TimeScale scale, bool isBlack) {
+ _screenFader->doFadeInSync(time, scale, isBlack);
+ _updatesEnabled = true;
+}
+
+void GraphicsManager::markCursorAsDirty() {
+ _modifiedScreen = true;
+}
+
+void GraphicsManager::newShakePoint(int32 index1, int32 index2, int32 maxRadius) {
+ int32 index3 = (index1 + index2) >> 1;
+
+ if (maxRadius == 0) {
+ _shakeOffsets[index3].x = ((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1);
+ _shakeOffsets[index3].y = ((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1);
+ } else {
+ double angle = (int32)(_vm->getRandomNumber(360 - 1) * 3.1415926535 / 180);
+ int32 radius = maxRadius;
+ _shakeOffsets[index3].x = (int32)(((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1) +
+ cos(angle) / 2 * radius);
+ _shakeOffsets[index3].y = (int32)(((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1) +
+ sin(angle) * radius);
+ }
+
+ if (index1 < index3 - 1)
+ newShakePoint(index1, index3, maxRadius * 2 / 3);
+
+ if (index3 < index2 - 1)
+ newShakePoint(index3, index2, maxRadius * 2 / 3);
+}
+
+void GraphicsManager::shakeTheWorld(TimeValue duration, TimeScale scale) {
+ if (duration == 0 || scale == 0)
+ return;
+
+ _shakeOffsets[0].x = 0;
+ _shakeOffsets[0].y = 0;
+ _shakeOffsets[(kMaxShakeOffsets - 1) / 4].x = 0;
+ _shakeOffsets[(kMaxShakeOffsets - 1) / 4].y = 0;
+ _shakeOffsets[(kMaxShakeOffsets - 1) / 2].x = 0;
+ _shakeOffsets[(kMaxShakeOffsets - 1) / 2].y = 0;
+ _shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].x = 0;
+ _shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].y = 0;
+ _shakeOffsets[kMaxShakeOffsets - 1].x = 0;
+ _shakeOffsets[kMaxShakeOffsets - 1].y = 0;
+
+ newShakePoint(0, (kMaxShakeOffsets - 1) / 4, 8);
+ newShakePoint((kMaxShakeOffsets - 1) / 4, (kMaxShakeOffsets - 1) / 2, 6);
+ newShakePoint((kMaxShakeOffsets - 1) / 2, (kMaxShakeOffsets - 1) * 3 / 4, 4);
+ newShakePoint((kMaxShakeOffsets - 1) * 3 / 4, kMaxShakeOffsets - 1, 3);
+
+ Common::Point lastOffset(0, 0);
+
+ // Store the current screen for later use
+ Graphics::Surface oldScreen;
+ Graphics::Surface *curScreen = g_system->lockScreen();
+ oldScreen.copyFrom(*curScreen);
+ g_system->unlockScreen();
+
+ // Convert to millis
+ duration = duration * 1000 / scale;
+
+ uint32 startTime = g_system->getMillis();
+
+ while (g_system->getMillis() < startTime + duration) {
+ Common::Point thisOffset = _shakeOffsets[(g_system->getMillis() - startTime) * (kMaxShakeOffsets - 1) / duration];
+ if (thisOffset != lastOffset) {
+ // Fill the screen with black
+ Graphics::Surface *screen = g_system->lockScreen();
+ screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0));
+ g_system->unlockScreen();
+
+ // Calculate the src/dst offsets and the width/height
+ int32 srcOffsetX, dstOffsetX, width;
+
+ if (thisOffset.x > 0) {
+ srcOffsetX = 0;
+ dstOffsetX = thisOffset.x;
+ width = 640 - dstOffsetX;
+ } else {
+ srcOffsetX = -thisOffset.x;
+ dstOffsetX = 0;
+ width = 640 - srcOffsetX;
+ }
+
+ int32 srcOffsetY, dstOffsetY, height;
+
+ if (thisOffset.y > 0) {
+ srcOffsetY = 0;
+ dstOffsetY = thisOffset.y;
+ height = 480 - dstOffsetY;
+ } else {
+ srcOffsetY = -thisOffset.y;
+ dstOffsetY = 0;
+ height = 480 - srcOffsetY;
+ }
+
+ // Now copy to the screen
+ g_system->copyRectToScreen((byte *)oldScreen.getBasePtr(srcOffsetX, srcOffsetY), oldScreen.pitch,
+ dstOffsetX, dstOffsetY, width, height);
+ g_system->updateScreen();
+
+ lastOffset = thisOffset;
+ }
+
+ g_system->delayMillis(10);
+ }
+
+ if (lastOffset.x != 0 || lastOffset.y != 0) {
+ g_system->copyRectToScreen((byte *)oldScreen.pixels, oldScreen.pitch, 0, 0, 640, 480);
+ g_system->updateScreen();
+ }
+
+ oldScreen.free();
+}
+
+void GraphicsManager::enableErase() {
+ _erase = true;
+}
+
+void GraphicsManager::disableErase() {
+ _erase = false;
+}
+
+void GraphicsManager::enableUpdates() {
+ _updatesEnabled = true;
+ _screenFader->setFaderValue(100);
+}
+
+void GraphicsManager::disableUpdates() {
+ _updatesEnabled = false;
+ _screenFader->setFaderValue(0);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/graphics.h b/engines/pegasus/graphics.h
new file mode 100644
index 0000000000..fcdf1c9e78
--- /dev/null
+++ b/engines/pegasus/graphics.h
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_GRAPHICS_H
+#define PEGASUS_GRAPHICS_H
+
+#include "common/rect.h"
+#include "common/str.h"
+#include "common/system.h"
+#include "graphics/surface.h"
+
+#include "pegasus/constants.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+class Cursor;
+class DisplayElement;
+class PegasusEngine;
+class ScreenFader;
+
+class GraphicsManager {
+friend class Cursor;
+public:
+ GraphicsManager(PegasusEngine *vm);
+ ~GraphicsManager();
+
+ void addDisplayElement(DisplayElement *element);
+ void removeDisplayElement(DisplayElement *element);
+ void invalRect(const Common::Rect &rect);
+ DisplayOrder getBackOfActiveLayer() const { return _backLayer; }
+ DisplayOrder getFrontOfActiveLayer() const { return _frontLayer; }
+ void updateDisplay();
+ Graphics::Surface *getCurSurface() { return _curSurface; }
+ void setCurSurface(Graphics::Surface *surface) { _curSurface = surface; }
+ Graphics::Surface *getWorkArea() { return &_workArea; }
+ void clearScreen();
+ DisplayElement *findDisplayElement(const DisplayElementID id);
+ void shakeTheWorld(TimeValue time, TimeScale scale);
+ void enableErase();
+ void disableErase();
+ void enableUpdates();
+ void disableUpdates();
+
+ // These default to black
+ void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true);
+ void doFadeInSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true);
+
+protected:
+ void markCursorAsDirty();
+
+private:
+ PegasusEngine *_vm;
+
+ bool _modifiedScreen, _erase;
+ Common::Rect _dirtyRect;
+ DisplayOrder _backLayer, _frontLayer;
+ DisplayElement *_firstDisplayElement, *_lastDisplayElement;
+ Graphics::Surface _workArea, *_curSurface;
+
+ // Shake Shake Shake!
+ static const int kMaxShakeOffsets = 17;
+ Common::Point _shakeOffsets[kMaxShakeOffsets];
+ void newShakePoint(int32 index1, int32 index2, int32 maxRadius);
+
+ bool _updatesEnabled;
+ ScreenFader *_screenFader;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/hotspot.cpp b/engines/pegasus/hotspot.cpp
new file mode 100644
index 0000000000..d8b07a94f4
--- /dev/null
+++ b/engines/pegasus/hotspot.cpp
@@ -0,0 +1,325 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/stream.h"
+
+#include "pegasus/hotspot.h"
+
+namespace Pegasus {
+
+Region::Region(Common::ReadStream *stream) {
+ uint16 length = stream->readUint16BE();
+
+ assert(length >= 10);
+
+ _bounds.top = stream->readUint16BE();
+ _bounds.left = stream->readUint16BE();
+ _bounds.bottom = stream->readUint16BE();
+ _bounds.right = stream->readUint16BE();
+
+ _bounds.debugPrint(0, "Bounds:");
+
+ if (length == 10)
+ return;
+
+ length -= 10;
+
+ while (length > 0) {
+ Vector v;
+ v.y = stream->readUint16BE();
+ length -= 2;
+
+ if (v.y == 0x7fff)
+ break;
+
+ debug(0, "y: %d", v.y);
+
+ // Normalize y to _bounds
+ v.y -= _bounds.top;
+
+ while (length > 0) {
+ Run run;
+ run.start = stream->readUint16BE();
+ length -= 2;
+
+ if (run.start == 0x7fff)
+ break;
+
+ run.end = stream->readUint16BE();
+ length -= 2;
+
+ debug(0, "\t[%d, %d)", run.start, run.end);
+
+ // Normalize to _bounds
+ run.start -= _bounds.left;
+ run.end -= _bounds.left;
+
+ v.push_back(run);
+ }
+
+ _vectors.push_back(v);
+ }
+}
+
+Region::Region(const Common::Rect &rect) {
+ _bounds = rect;
+}
+
+bool Region::pointInRegion(const Common::Point &point) const {
+ if (!_bounds.contains(point))
+ return false;
+
+ bool pixelActive = false;
+
+ // Normalize the points to _bounds
+ uint16 x = point.x - _bounds.left;
+ uint16 y = point.y - _bounds.top;
+
+ for (Common::List<Vector>::const_iterator v = _vectors.begin(); v != _vectors.end(); v++) {
+ if (v->y > y)
+ return pixelActive;
+
+ for (Vector::const_iterator run = v->begin(); run != v->end(); run++) {
+ if (x >= run->start && x < run->end) {
+ pixelActive = !pixelActive;
+ break;
+ }
+ }
+ }
+
+ // the case if the region is just a rect
+ return true;
+}
+
+void Region::moveTo(CoordType h, CoordType v) {
+ _bounds.moveTo(h, v);
+}
+
+void Region::moveTo(const Common::Point &point) {
+ _bounds.moveTo(point);
+}
+
+void Region::translate(CoordType h, CoordType v) {
+ _bounds.translate(h, v);
+}
+
+void Region::translate(const Common::Point &point) {
+ _bounds.translate(point.x, point.y);
+}
+
+void Region::getCenter(CoordType &h, CoordType &v) const {
+ h = (_bounds.left + _bounds.right) / 2;
+ v = (_bounds.top + _bounds.bottom) / 2;
+}
+
+void Region::getCenter(Common::Point &point) const {
+ getCenter(point.x, point.y);
+}
+
+Hotspot::Hotspot(const HotSpotID id) : IDObject(id) {
+ _spotFlags = kNoHotSpotFlags;
+ _spotActive = false;
+}
+
+Hotspot::~Hotspot() {
+}
+
+void Hotspot::setArea(const Common::Rect &area) {
+ _spotArea = Region(area);
+}
+
+void Hotspot::setArea(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) {
+ _spotArea = Region(Common::Rect(left, top, right, bottom));
+}
+
+void Hotspot::getBoundingBox(Common::Rect &r) const {
+ r = _spotArea.getBoundingBox();
+}
+
+void Hotspot::getCenter(Common::Point &pt) const {
+ _spotArea.getCenter(pt);
+}
+
+void Hotspot::getCenter(CoordType &h, CoordType &v) const {
+ _spotArea.getCenter(h, v);
+}
+
+void Hotspot::setActive() {
+ _spotActive = true;
+}
+
+void Hotspot::setInactive() {
+ _spotActive = false;
+}
+
+void Hotspot::setHotspotFlags(const HotSpotFlags flags) {
+ _spotFlags = flags;
+}
+
+void Hotspot::setMaskedHotspotFlags(const HotSpotFlags flags, const HotSpotFlags mask) {
+ _spotFlags = (_spotFlags & ~mask) | flags;
+}
+
+bool Hotspot::isSpotActive() const {
+ return _spotActive;
+}
+
+void Hotspot::moveSpotTo(const CoordType h, const CoordType v) {
+ _spotArea.moveTo(h, v);
+}
+
+void Hotspot::moveSpotTo(const Common::Point pt) {
+ _spotArea.moveTo(pt);
+}
+
+void Hotspot::moveSpot(const CoordType h, const CoordType v) {
+ _spotArea.translate(h, v);
+}
+
+void Hotspot::moveSpot(const Common::Point pt) {
+ _spotArea.translate(pt.x, pt.y);
+}
+
+bool Hotspot::pointInSpot(const Common::Point where) const {
+ return _spotActive && _spotArea.pointInRegion(where);
+}
+
+HotSpotFlags Hotspot::getHotspotFlags() const {
+ return _spotFlags;
+}
+
+HotspotList::HotspotList() {
+}
+
+HotspotList::~HotspotList() {
+ // TODO: Should this call deleteHotspots()?
+}
+
+void HotspotList::deleteHotspots() {
+ for (HotspotIterator it = begin(); it != end(); it++)
+ delete *it;
+
+ clear();
+}
+
+Hotspot *HotspotList::findHotspot(const Common::Point where) {
+ for (HotspotIterator it = begin(); it != end(); it++)
+ if ((*it)->pointInSpot(where))
+ return *it;
+
+ return 0;
+}
+
+HotSpotID HotspotList::findHotspotID(const Common::Point where) {
+ Hotspot *hotspot = findHotspot(where);
+ return hotspot ? hotspot->getObjectID() : kNoHotSpotID;
+}
+
+Hotspot *HotspotList::findHotspotByID(const HotSpotID id) {
+ for (HotspotIterator it = begin(); it != end(); it++)
+ if ((*it)->getObjectID() == id)
+ return *it;
+
+ return 0;
+}
+
+Hotspot *HotspotList::findHotspotByMask(const HotSpotFlags flags) {
+ for (HotspotIterator it = begin(); it != end(); it++)
+ if (((*it)->getHotspotFlags() & flags) == flags)
+ return *it;
+
+ return 0;
+}
+
+void HotspotList::activateMaskedHotspots(const HotSpotFlags flags) {
+ for (HotspotIterator it = begin(); it != end(); it++)
+ if (flags == kNoHotSpotFlags || ((*it)->getHotspotFlags() & flags) != 0)
+ (*it)->setActive();
+}
+
+void HotspotList::deactivateAllHotspots() {
+ for (HotspotIterator it = begin(); it != end(); it++)
+ (*it)->setInactive();
+}
+
+void HotspotList::deactivateMaskedHotspots(const HotSpotFlags flags) {
+ for (HotspotIterator it = begin(); it != end(); it++)
+ if (((*it)->getHotspotFlags() & flags) != 0)
+ (*it)->setInactive();
+}
+
+void HotspotList::activateOneHotspot(const HotSpotID id) {
+ for (HotspotIterator it = begin(); it != end(); it++) {
+ if ((*it)->getObjectID() == id) {
+ (*it)->setActive();
+ return;
+ }
+ }
+}
+
+void HotspotList::deactivateOneHotspot(const HotSpotID id) {
+ for (HotspotIterator it = begin(); it != end(); it++) {
+ if ((*it)->getObjectID() == id) {
+ (*it)->setInactive();
+ return;
+ }
+ }
+}
+
+void HotspotList::removeOneHotspot(const HotSpotID id) {
+ for (HotspotIterator it = begin(); it != end(); it++) {
+ if ((*it)->getObjectID() == id) {
+ erase(it);
+ return;
+ }
+ }
+}
+
+void HotspotList::removeMaskedHotspots(const HotSpotFlags flags) {
+ if (flags != kNoHotSpotFlags) {
+ for (HotspotIterator it = begin(); it != end(); ) {
+ if (((*it)->getHotspotFlags() & flags) != 0)
+ it = erase(it);
+ else
+ it++;
+ }
+ } else {
+ clear();
+ }
+}
+
+void HotspotList::setHotspotRect(const HotSpotID id, const Common::Rect &r) {
+ Hotspot *hotspot = findHotspotByID(id);
+ if (hotspot)
+ hotspot->setArea(r);
+}
+
+void HotspotList::getHotspotRect(const HotSpotID id, Common::Rect &r) {
+ Hotspot *hotspot = findHotspotByID(id);
+ if (hotspot)
+ hotspot->getBoundingBox(r);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/hotspot.h b/engines/pegasus/hotspot.h
new file mode 100644
index 0000000000..64d3fb19f9
--- /dev/null
+++ b/engines/pegasus/hotspot.h
@@ -0,0 +1,152 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_HOTSPOT_H
+#define PEGASUS_HOTSPOT_H
+
+#include "common/list.h"
+#include "common/rect.h"
+
+#include "pegasus/constants.h"
+#include "pegasus/types.h"
+#include "pegasus/util.h"
+
+/*
+
+ Hot spots combine a pixel area, an ID value and an active flag.
+
+ A point is considered in a hot spot if the point is in the hot spot's pixel area and
+ the active flag is set.
+
+ In addition, hot spots have a 32 bit word of bit flags for filtering use.
+
+*/
+
+namespace Common {
+ class ReadStream;
+}
+
+namespace Pegasus {
+
+// Our implementation of QuickDraw regions
+class Region {
+public:
+ Region() {}
+ Region(Common::ReadStream *stream);
+ Region(const Common::Rect &rect);
+
+ Common::Rect getBoundingBox() const { return _bounds; }
+
+ bool pointInRegion(const Common::Point &point) const;
+
+ void moveTo(CoordType h, CoordType v);
+ void moveTo(const Common::Point &point);
+ void translate(CoordType h, CoordType v);
+ void translate(const Common::Point &point);
+ void getCenter(CoordType &h, CoordType &v) const;
+ void getCenter(Common::Point &point) const;
+
+private:
+ Common::Rect _bounds;
+
+ struct Run {
+ uint16 start, end;
+ };
+
+ class Vector : public Common::List<Run> {
+ public:
+ uint16 y;
+ };
+
+ Common::List<Vector> _vectors;
+};
+
+class Hotspot : public IDObject {
+public:
+ Hotspot(const HotSpotID);
+ virtual ~Hotspot();
+
+ void setArea(const Region &region) { _spotArea = region; }
+ void setArea(const Common::Rect &);
+ void setArea(const CoordType, const CoordType, const CoordType, const CoordType);
+ void getBoundingBox(Common::Rect &) const;
+ void getArea(Region &) const;
+ void getCenter(Common::Point&) const;
+ void getCenter(CoordType&, CoordType&) const;
+
+ void moveSpotTo(const CoordType, const CoordType);
+ void moveSpotTo(const Common::Point);
+ void moveSpot(const CoordType, const CoordType);
+ void moveSpot(const Common::Point);
+
+ bool pointInSpot(const Common::Point) const;
+
+ void setActive();
+ void setInactive();
+ bool isSpotActive() const;
+
+ HotSpotFlags getHotspotFlags() const;
+ void setHotspotFlags(const HotSpotFlags);
+ void setMaskedHotspotFlags(const HotSpotFlags flags, const HotSpotFlags mask);
+
+protected:
+ Region _spotArea;
+ HotSpotFlags _spotFlags;
+ bool _spotActive;
+};
+
+class HotspotList : public Common::List<Hotspot *> {
+public:
+ HotspotList();
+ virtual ~HotspotList();
+
+ void deleteHotspots();
+
+ Hotspot *findHotspot(const Common::Point);
+ HotSpotID findHotspotID(const Common::Point);
+ Hotspot *findHotspotByID(const HotSpotID);
+ Hotspot *findHotspotByMask(const HotSpotFlags);
+
+ void activateMaskedHotspots(const HotSpotFlags = kNoHotSpotFlags);
+ void deactivateAllHotspots();
+ void deactivateMaskedHotspots(const HotSpotFlags);
+
+ void activateOneHotspot(const HotSpotID);
+ void deactivateOneHotspot(const HotSpotID);
+
+ void removeOneHotspot(const HotSpotID);
+ void removeMaskedHotspots(const HotSpotFlags = kNoHotSpotFlags);
+
+ void setHotspotRect(const HotSpotID, const Common::Rect&);
+ void getHotspotRect(const HotSpotID, Common::Rect&);
+};
+
+typedef HotspotList::iterator HotspotIterator;
+
+#define g_allHotspots (((PegasusEngine *)g_engine)->getAllHotspots())
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/input.cpp b/engines/pegasus/input.cpp
new file mode 100644
index 0000000000..b74e4a4c45
--- /dev/null
+++ b/engines/pegasus/input.cpp
@@ -0,0 +1,373 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/events.h"
+#include "common/system.h"
+
+#include "pegasus/cursor.h"
+#include "pegasus/input.h"
+#include "pegasus/pegasus.h"
+
+namespace Common {
+DECLARE_SINGLETON(Pegasus::InputDeviceManager);
+}
+
+namespace Pegasus {
+
+InputDeviceManager::InputDeviceManager() {
+ // Set all keys to "not down"
+ _keyMap[Common::KEYCODE_UP] = false;
+ _keyMap[Common::KEYCODE_KP8] = false;
+ _keyMap[Common::KEYCODE_LEFT] = false;
+ _keyMap[Common::KEYCODE_KP4] = false;
+ _keyMap[Common::KEYCODE_DOWN] = false;
+ _keyMap[Common::KEYCODE_KP5] = false;
+ _keyMap[Common::KEYCODE_RIGHT] = false;
+ _keyMap[Common::KEYCODE_KP6] = false;
+ _keyMap[Common::KEYCODE_RETURN] = false;
+ _keyMap[Common::KEYCODE_SPACE] = false;
+ _keyMap[Common::KEYCODE_t] = false;
+ _keyMap[Common::KEYCODE_KP_EQUALS] = false;
+ _keyMap[Common::KEYCODE_i] = false;
+ _keyMap[Common::KEYCODE_KP_DIVIDE] = false;
+ _keyMap[Common::KEYCODE_q] = false;
+ _keyMap[Common::KEYCODE_ESCAPE] = false;
+ _keyMap[Common::KEYCODE_p] = false;
+ _keyMap[Common::KEYCODE_TILDE] = false;
+ _keyMap[Common::KEYCODE_BACKQUOTE] = false;
+ _keyMap[Common::KEYCODE_NUMLOCK] = false;
+ _keyMap[Common::KEYCODE_BACKSPACE] = false;
+ _keyMap[Common::KEYCODE_KP_MULTIPLY] = false;
+ _keyMap[Common::KEYCODE_LALT] = false;
+ _keyMap[Common::KEYCODE_RALT] = false;
+ _keyMap[Common::KEYCODE_e] = false;
+ _keyMap[Common::KEYCODE_KP_ENTER] = false;
+
+ g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 2, false);
+ _lastRawBits = kAllUpBits;
+ _consoleRequested = false;
+}
+
+InputDeviceManager::~InputDeviceManager() {
+ g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
+}
+
+void InputDeviceManager::getInput(Input &input, const InputBits filter) {
+ // Poll for events, but ignore them!
+ // We'll pick them up in notifyEvent()
+ // We do that so that any pollEvent() call can update the variables
+ // (ie. if one uses enter to access the restore menu, we never receive
+ // the key up event, which leads to bad things)
+ // This is to closely emulate what the GetKeys() function did on Mac OS
+ Common::Event event;
+ while (g_system->getEventManager()->pollEvent(event))
+ ;
+
+ // Now create the bitfield
+ InputBits currentBits = 0;
+
+ if (_keyMap[Common::KEYCODE_UP] || _keyMap[Common::KEYCODE_KP8])
+ currentBits |= (kRawButtonDown << kUpButtonShift);
+
+ if (_keyMap[Common::KEYCODE_DOWN] || _keyMap[Common::KEYCODE_KP5])
+ currentBits |= (kRawButtonDown << kDownButtonShift);
+
+ if (_keyMap[Common::KEYCODE_LEFT] || _keyMap[Common::KEYCODE_KP4])
+ currentBits |= (kRawButtonDown << kLeftButtonShift);
+
+ if (_keyMap[Common::KEYCODE_RIGHT] || _keyMap[Common::KEYCODE_KP6])
+ currentBits |= (kRawButtonDown << kRightButtonShift);
+
+ if (_keyMap[Common::KEYCODE_SPACE] || _keyMap[Common::KEYCODE_RETURN] || _keyMap[Common::KEYCODE_KP_ENTER])
+ currentBits |= (kRawButtonDown << kTwoButtonShift);
+
+ if (_keyMap[Common::KEYCODE_t] || _keyMap[Common::KEYCODE_KP_EQUALS])
+ currentBits |= (kRawButtonDown << kThreeButtonShift);
+
+ if (_keyMap[Common::KEYCODE_i] || _keyMap[Common::KEYCODE_KP_DIVIDE])
+ currentBits |= (kRawButtonDown << kFourButtonShift);
+
+ if (_keyMap[Common::KEYCODE_q])
+ currentBits |= (kRawButtonDown << kMod1ButtonShift);
+
+ if (_keyMap[Common::KEYCODE_ESCAPE] || _keyMap[Common::KEYCODE_p])
+ currentBits |= (kRawButtonDown << kMod3ButtonShift);
+
+ if (_keyMap[Common::KEYCODE_TILDE] || _keyMap[Common::KEYCODE_BACKQUOTE] || _keyMap[Common::KEYCODE_NUMLOCK])
+ currentBits |= (kRawButtonDown << kLeftFireButtonShift);
+
+ if (_keyMap[Common::KEYCODE_BACKSPACE] || _keyMap[Common::KEYCODE_KP_MULTIPLY])
+ currentBits |= (kRawButtonDown << kRightFireButtonShift);
+
+ // Update mouse button state
+ // Note that we don't use EVENT_LBUTTONUP/EVENT_LBUTTONDOWN because
+ // they do not show if the button is being held down. We're treating
+ // both mouse buttons as the same for ease of use.
+ if (g_system->getEventManager()->getButtonState() != 0)
+ currentBits |= (kRawButtonDown << kTwoButtonShift);
+
+ // Update the mouse position too
+ input.setInputLocation(g_system->getEventManager()->getMousePos());
+
+ // Set the outgoing bits
+ InputBits filteredBits = currentBits & filter;
+ input.setInputBits((filteredBits & kAllButtonDownBits) | (filteredBits & _lastRawBits & kAllAutoBits));
+
+ // Update the last bits
+ _lastRawBits = currentBits;
+
+ // Set the console to be requested or not
+ input.setConsoleRequested(_consoleRequested);
+
+ // WORKAROUND: The original had this in currentBits, but then
+ // pressing alt would count as an event (and mess up someone
+ // trying to do alt+enter or something). Since it's only used
+ // as an easter egg, I'm just going to handle it as a separate
+ // bool value.
+ // WORKAROUND x2: I'm also accepting 'e' here since an
+ // alt+click is often intercepted by the OS. 'e' is used as the
+ // easter egg key in Buried in Time and Legacy of Time.
+ input.setAltDown(_keyMap[Common::KEYCODE_LALT] || _keyMap[Common::KEYCODE_RALT] || _keyMap[Common::KEYCODE_e]);
+}
+
+// Wait until the input device stops returning input allowed by filter...
+void InputDeviceManager::waitInput(const InputBits filter) {
+ if (filter != 0) {
+ for (;;) {
+ Input input;
+ getInput(input, filter);
+ if (!input.anyInput())
+ break;
+ }
+ }
+}
+
+bool InputDeviceManager::notifyEvent(const Common::Event &event) {
+ // We're mapping from ScummVM events to pegasus events, which
+ // are based on pippin events.
+ _consoleRequested = false;
+
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_d:
+ if (event.kbd.flags & Common::KBD_CTRL) // Console!
+ _consoleRequested = true;
+ break;
+ case Common::KEYCODE_s:
+ // We support meta where available and control elsewhere
+ if (event.kbd.flags & (Common::KBD_CTRL|Common::KBD_META))
+ ((PegasusEngine *)g_engine)->requestSave();
+ break;
+ case Common::KEYCODE_o: // o for open (original)
+ case Common::KEYCODE_l: // l for load (ScummVM terminology)
+ // We support meta where available and control elsewhere
+ if (event.kbd.flags & (Common::KBD_CTRL|Common::KBD_META))
+ ((PegasusEngine *)g_engine)->requestLoad();
+ break;
+ default:
+ // Otherwise, set the key to down if we have it
+ if (_keyMap.contains(event.kbd.keycode))
+ _keyMap[event.kbd.keycode] = true;
+ break;
+ }
+ break;
+ case Common::EVENT_KEYUP:
+ // Set the key to up if we have it
+ if (_keyMap.contains(event.kbd.keycode))
+ _keyMap[event.kbd.keycode] = false;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+int operator==(const Input &arg1, const Input &arg2) {
+ return arg1._inputState == arg2._inputState;
+}
+
+int operator!=(const Input &arg1, const Input &arg2) {
+ return !operator==(arg1, arg2);
+}
+
+InputHandler *InputHandler::_inputHandler = 0;
+bool InputHandler::_invalHotspots = false;
+InputBits InputHandler::_lastFilter = kFilterNoInput;
+
+InputHandler *InputHandler::setInputHandler(InputHandler *currentHandler) {
+ InputHandler *result = 0;
+
+ if (_inputHandler != currentHandler && (!_inputHandler || _inputHandler->releaseInputFocus())) {
+ result = _inputHandler;
+ _inputHandler = currentHandler;
+ if (_inputHandler)
+ _inputHandler->grabInputFocus();
+ }
+
+ return result;
+}
+
+void InputHandler::pollForInput() {
+ if (_inputHandler) {
+ Input input;
+ Hotspot *cursorSpot = 0;
+
+ InputHandler::getInput(input, cursorSpot);
+ if (_inputHandler->isClickInput(input, cursorSpot))
+ _inputHandler->clickInHotspot(input, cursorSpot);
+ else
+ _inputHandler->handleInput(input, cursorSpot);
+ }
+}
+
+void InputHandler::getInput(Input &input, Hotspot *&cursorSpot) {
+ Cursor *cursor = ((PegasusEngine *)g_engine)->_cursor;
+
+ if (_inputHandler)
+ _lastFilter = _inputHandler->getInputFilter();
+ else
+ _lastFilter = kFilterAllInput;
+
+ InputDevice.getInput(input, _lastFilter);
+
+ if (_inputHandler && _inputHandler->wantsCursor() && (_lastFilter & _inputHandler->getClickFilter()) != 0) {
+ if (cursor->isVisible()) {
+ g_allHotspots.deactivateAllHotspots();
+ _inputHandler->activateHotspots();
+
+ Common::Point cursorLocation;
+ cursor->getCursorLocation(cursorLocation);
+ cursorSpot = g_allHotspots.findHotspot(cursorLocation);
+
+ if (_inputHandler)
+ _inputHandler->updateCursor(cursorLocation, cursorSpot);
+ } else {
+ cursor->hideUntilMoved();
+ }
+ } else {
+ cursor->hide();
+ }
+}
+
+void InputHandler::readInputDevice(Input &input) {
+ InputDevice.getInput(input, kFilterAllInput);
+}
+
+InputHandler::InputHandler(InputHandler *nextHandler) {
+ _nextHandler = nextHandler;
+ allowInput(true);
+}
+
+InputHandler::~InputHandler() {
+ if (_inputHandler == this)
+ setInputHandler(_nextHandler);
+}
+
+void InputHandler::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (_nextHandler)
+ _nextHandler->handleInput(input, cursorSpot);
+}
+
+void InputHandler::clickInHotspot(const Input &input, const Hotspot *cursorSpot) {
+ if (_nextHandler)
+ _nextHandler->clickInHotspot(input, cursorSpot);
+}
+
+bool InputHandler::isClickInput(const Input &input, const Hotspot *cursorSpot) {
+ if (_nextHandler)
+ return _nextHandler->isClickInput(input, cursorSpot);
+
+ return false;
+}
+
+void InputHandler::activateHotspots() {
+ if (_nextHandler)
+ _nextHandler->activateHotspots();
+}
+
+InputBits InputHandler::getInputFilter() {
+ if (_allowInput) {
+ if (_nextHandler)
+ return _nextHandler->getInputFilter();
+ else
+ return kFilterAllInput;
+ }
+
+ return kFilterNoInput;
+}
+
+InputBits InputHandler::getClickFilter() {
+ if (_allowInput && _nextHandler)
+ return _nextHandler->getClickFilter();
+
+ return kFilterNoInput;
+}
+
+void InputHandler::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) {
+ if (_nextHandler)
+ _nextHandler->updateCursor(cursorLocation, cursorSpot);
+}
+
+bool InputHandler::wantsCursor() {
+ if (_allowInput) {
+ if (_nextHandler)
+ return _nextHandler->wantsCursor();
+ else
+ return true;
+ }
+
+ return false;
+}
+
+Tracker *Tracker::_currentTracker = 0;
+
+void Tracker::handleInput(const Input &input, const Hotspot *) {
+ if (stopTrackingInput(input))
+ stopTracking(input);
+ else if (isTracking())
+ continueTracking(input);
+}
+
+void Tracker::startTracking(const Input &) {
+ if (!isTracking()) {
+ _savedHandler = InputHandler::setInputHandler(this);
+ _currentTracker = this;
+ }
+}
+
+void Tracker::stopTracking(const Input &) {
+ if (isTracking()) {
+ _currentTracker = NULL;
+ InputHandler::setInputHandler(_savedHandler);
+ }
+}
+
+bool Tracker::isClickInput(const Input &input, const Hotspot *hotspot) {
+ return !isTracking() && InputHandler::isClickInput(input, hotspot);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/input.h b/engines/pegasus/input.h
new file mode 100644
index 0000000000..3e938fa42a
--- /dev/null
+++ b/engines/pegasus/input.h
@@ -0,0 +1,500 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_INPUT_H
+#define PEGASUS_INPUT_H
+
+#include "common/events.h"
+#include "common/hashmap.h"
+#include "common/rect.h"
+#include "common/singleton.h"
+
+#include "pegasus/constants.h"
+#include "pegasus/types.h"
+
+namespace Pegasus {
+
+class Hotspot;
+class Input;
+
+class InputDeviceManager : public Common::Singleton<InputDeviceManager>, public Common::EventObserver {
+public:
+ InputDeviceManager();
+ ~InputDeviceManager();
+
+ bool notifyEvent(const Common::Event &event);
+
+ void getInput(Input &, const InputBits);
+
+ void waitInput(const InputBits);
+
+protected:
+ friend class Common::Singleton<SingletonBaseType>;
+
+ // Keep track of which keys are down (= true) or not
+ Common::HashMap<uint, bool> _keyMap;
+ InputBits _lastRawBits;
+ bool _consoleRequested;
+};
+
+enum {
+ kButtonDownBit = 0,
+ kAutoButtonBit = 1,
+ kBitsPerButton = 2,
+
+ kButtonDownMask = 1 << kButtonDownBit,
+ kAutoButtonMask = 1 << kAutoButtonBit,
+
+ kButtonStateBits = kButtonDownMask | kAutoButtonMask,
+
+ kRawButtonUp = 0,
+ kRawButtonDown = kButtonDownMask | kAutoButtonMask,
+
+ kButtonUp = 0,
+ kButtonDown = kButtonDownMask,
+ kButtonAutoUp = kAutoButtonMask,
+ kButtonAutoDown = kButtonDownMask | kAutoButtonMask
+};
+
+enum {
+ kUpButtonNum = 0,
+ kLeftButtonNum = 1,
+ kDownButtonNum = 2,
+ kRightButtonNum = 3,
+ kLeftFireButtonNum = 4,
+ kRightFireButtonNum = 5,
+ kOneButtonNum = 6,
+ kTwoButtonNum = 7,
+ kThreeButtonNum = 8,
+ kFourButtonNum = 9,
+ kMod1ButtonNum = 10,
+ kMod2ButtonNum = 11,
+ kMod3ButtonNum = 12
+};
+
+enum {
+ kUpButtonShift = kUpButtonNum * kBitsPerButton,
+ kLeftButtonShift = kLeftButtonNum * kBitsPerButton,
+ kDownButtonShift = kDownButtonNum * kBitsPerButton,
+ kRightButtonShift = kRightButtonNum * kBitsPerButton,
+ kLeftFireButtonShift = kLeftFireButtonNum * kBitsPerButton,
+ kRightFireButtonShift = kRightFireButtonNum * kBitsPerButton,
+ kOneButtonShift = kOneButtonNum * kBitsPerButton,
+ kTwoButtonShift = kTwoButtonNum * kBitsPerButton,
+ kThreeButtonShift = kThreeButtonNum * kBitsPerButton,
+ kFourButtonShift = kFourButtonNum * kBitsPerButton,
+ kMod1ButtonShift = kMod1ButtonNum * kBitsPerButton,
+ kMod2ButtonShift = kMod2ButtonNum * kBitsPerButton,
+ kMod3ButtonShift = kMod3ButtonNum * kBitsPerButton
+};
+
+enum {
+ kAllUpBits = (kButtonUp << kUpButtonShift) |
+ (kButtonUp << kLeftButtonShift) |
+ (kButtonUp << kDownButtonShift) |
+ (kButtonUp << kRightButtonShift) |
+ (kButtonUp << kLeftFireButtonShift) |
+ (kButtonUp << kRightFireButtonShift) |
+ (kButtonUp << kOneButtonShift) |
+ (kButtonUp << kTwoButtonShift) |
+ (kButtonUp << kThreeButtonShift) |
+ (kButtonUp << kFourButtonShift) |
+ (kButtonUp << kMod1ButtonShift) |
+ (kButtonUp << kMod2ButtonShift) |
+ (kButtonUp << kMod3ButtonShift),
+ kDirectionBits = (kButtonDownMask << kUpButtonShift) |
+ (kButtonDownMask << kLeftButtonShift) |
+ (kButtonDownMask << kDownButtonShift) |
+ (kButtonDownMask << kRightButtonShift),
+ kButtonBits = (kButtonDownMask << kLeftFireButtonShift) |
+ (kButtonDownMask << kRightFireButtonShift) |
+ (kButtonDownMask << kOneButtonShift) |
+ (kButtonDownMask << kTwoButtonShift) |
+ (kButtonDownMask << kThreeButtonShift) |
+ (kButtonDownMask << kFourButtonShift) |
+ (kButtonDownMask << kMod1ButtonShift) |
+ (kButtonDownMask << kMod2ButtonShift) |
+ (kButtonDownMask << kMod3ButtonShift),
+ kAllButtonDownBits = kDirectionBits | kButtonBits,
+ kAllAutoBits = (kAutoButtonMask << kUpButtonShift) |
+ (kAutoButtonMask << kLeftButtonShift) |
+ (kAutoButtonMask << kDownButtonShift) |
+ (kAutoButtonMask << kRightButtonShift) |
+ (kAutoButtonMask << kLeftFireButtonShift) |
+ (kAutoButtonMask << kRightFireButtonShift) |
+ (kAutoButtonMask << kOneButtonShift) |
+ (kAutoButtonMask << kTwoButtonShift) |
+ (kAutoButtonMask << kThreeButtonShift) |
+ (kAutoButtonMask << kFourButtonShift) |
+ (kAutoButtonMask << kMod1ButtonShift) |
+ (kAutoButtonMask << kMod2ButtonShift) |
+ (kAutoButtonMask << kMod3ButtonShift),
+
+ kFilterUpButton = kButtonDownMask << kUpButtonShift,
+ kFilterUpAuto = kAutoButtonMask << kUpButtonShift,
+ kFilterUpButtonAny = kFilterUpButton | kFilterUpAuto,
+ kFilterLeftButton = kButtonDownMask << kLeftButtonShift,
+ kFilterLeftAuto = kAutoButtonMask << kLeftButtonShift,
+ kFilterLeftButtonAny = kFilterLeftButton | kFilterLeftAuto,
+ kFilterDownButton = kButtonDownMask << kDownButtonShift,
+ kFilterDownAuto = kAutoButtonMask << kDownButtonShift,
+ kFilterDownButtonAny = kFilterDownButton | kFilterDownAuto,
+ kFilterRightButton = kButtonDownMask << kRightButtonShift,
+ kFilterRightAuto = kAutoButtonMask << kRightButtonShift,
+ kFilterRightButtonAny = kFilterRightButton | kFilterRightAuto,
+ kFilterLeftFireButton = kButtonDownMask << kLeftFireButtonShift,
+ kFilterLeftFireAuto = kAutoButtonMask << kLeftFireButtonShift,
+ kFilterLeftFireButtonAny = kFilterLeftFireButton | kFilterLeftFireAuto,
+ kFilterRightFireButton = kButtonDownMask << kRightFireButtonShift,
+ kFilterRightFireAuto = kAutoButtonMask << kRightFireButtonShift,
+ kFilterRightFireButtonAny = kFilterRightFireButton | kFilterRightFireAuto,
+ kFilterOneButton = kButtonDownMask << kOneButtonShift,
+ kFilterOneAuto = kAutoButtonMask << kOneButtonShift,
+ kFilterOneButtonAny = kFilterOneButton | kFilterOneAuto,
+ kFilterTwoButton = kButtonDownMask << kTwoButtonShift,
+ kFilterTwoAuto = kAutoButtonMask << kTwoButtonShift,
+ kFilterTwoButtonAny = kFilterTwoButton | kFilterTwoAuto,
+ kFilterThreeButton = kButtonDownMask << kThreeButtonShift,
+ kFilterThreeAuto = kAutoButtonMask << kThreeButtonShift,
+ kFilterThreeButtonAny = kFilterThreeButton | kFilterThreeAuto,
+ kFilterFourButton = kButtonDownMask << kFourButtonShift,
+ kFilterFourAuto = kAutoButtonMask << kFourButtonShift,
+ kFilterFourButtonAny = kFilterFourButton | kFilterFourAuto,
+ kFilterMod1Button = kButtonDownMask << kMod1ButtonShift,
+ kFilterMod1Auto = kAutoButtonMask << kMod1ButtonShift,
+ kFilterMod1ButtonAny = kFilterMod1Button | kFilterMod1Auto,
+ kFilterMod2Button = kButtonDownMask << kMod2ButtonShift,
+ kFilterMod2Auto = kAutoButtonMask << kMod2ButtonShift,
+ kFilterMod2ButtonAny = kFilterMod2Button | kFilterMod2Auto,
+ kFilterMod3Button = kButtonDownMask << kMod3ButtonShift,
+ kFilterMod3Auto = kAutoButtonMask << kMod3ButtonShift,
+ kFilterMod3ButtonAny = kFilterMod3Button | kFilterMod3Auto,
+
+ kFilterNoInput = 0,
+ kFilterAllInput = kFilterUpButton |
+ kFilterUpAuto |
+ kFilterLeftButton |
+ kFilterLeftAuto |
+ kFilterDownButton |
+ kFilterDownAuto |
+ kFilterRightButton |
+ kFilterRightAuto |
+ kFilterLeftFireButton |
+ kFilterLeftFireAuto |
+ kFilterRightFireButton |
+ kFilterRightFireAuto |
+ kFilterOneButton |
+ kFilterOneAuto |
+ kFilterTwoButton |
+ kFilterTwoAuto |
+ kFilterThreeButton |
+ kFilterThreeAuto |
+ kFilterFourButton |
+ kFilterFourAuto |
+ kFilterMod1Button |
+ kFilterMod1Auto |
+ kFilterMod2Button |
+ kFilterMod2Auto |
+ kFilterMod3Button |
+ kFilterMod3Auto,
+
+ kFilterAllDirections = kFilterUpButton |
+ kFilterUpAuto |
+ kFilterLeftButton |
+ kFilterLeftAuto |
+ kFilterDownButton |
+ kFilterDownAuto |
+ kFilterRightButton |
+ kFilterRightAuto,
+
+ kFilterButtons = kFilterOneButton |
+ kFilterOneAuto |
+ kFilterTwoButton |
+ kFilterTwoAuto |
+ kFilterThreeButton |
+ kFilterThreeAuto |
+ kFilterFourButton |
+ kFilterFourAuto,
+
+ kFilterFireButtons = kFilterLeftFireButton |
+ kFilterLeftFireAuto |
+ kFilterRightFireButton |
+ kFilterRightFireAuto,
+
+ kFilterAllButtons = kFilterLeftFireButton |
+ kFilterLeftFireAuto |
+ kFilterRightFireButton |
+ kFilterRightFireAuto |
+ kFilterOneButton |
+ kFilterOneAuto |
+ kFilterTwoButton |
+ kFilterTwoAuto |
+ kFilterThreeButton |
+ kFilterThreeAuto |
+ kFilterFourButton |
+ kFilterFourAuto |
+ kFilterMod1Button |
+ kFilterMod1Auto |
+ kFilterMod2Button |
+ kFilterMod2Auto |
+ kFilterMod3Button |
+ kFilterMod3Auto,
+
+ kFilterAllInputNoAuto = kFilterUpButton |
+ kFilterLeftButton |
+ kFilterDownButton |
+ kFilterRightButton |
+ kFilterLeftFireButton |
+ kFilterRightFireButton |
+ kFilterOneButton |
+ kFilterTwoButton |
+ kFilterThreeButton |
+ kFilterFourButton |
+ kFilterMod1Button |
+ kFilterMod2Button |
+ kFilterMod3Button
+};
+
+static const InputBits kHintInterruption = kFilterAllInputNoAuto;
+static const InputBits kWarningInterruption = kFilterNoInput;
+static const InputBits kOpticalInterruption = kFilterAllInputNoAuto;
+
+/*
+
+ Buttons are defined as:
+ up, left, down, right direction buttons.
+ fireLeft, fireRight: fire buttons.
+ mod1, mod2, mod3: modifier buttons, similar to shift, control, etc.
+ a, b, c, d: general purpose buttons.
+
+ button state is held as bits in a long word, two bits per button.
+
+ Filter bits:
+ for each button, two bits are assigned for filtering. If bit 0 is set, the
+ corresponding button is available for "button down" input. If bit 1 is set,
+ the corresponding button is available for "auto down" input. Note that bit
+ 1 is meaningful only if bit 0 is set.
+
+*/
+
+class Input {
+friend int operator==(const Input &, const Input &);
+friend int operator!=(const Input &, const Input &);
+friend class InputDeviceManager;
+
+public:
+ Input() { clearInput(); }
+
+ bool upButtonDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonDown << kUpButtonShift); }
+ bool upButtonAutoDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonAutoDown << kUpButtonShift); }
+ bool upButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kUpButtonShift)) != 0; }
+
+ bool leftButtonDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonDown << kLeftButtonShift); }
+ bool leftButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonAutoDown << kLeftButtonShift); }
+ bool leftButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftButtonShift)) != 0; }
+
+ bool downButtonDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonDown << kDownButtonShift); }
+ bool downButtonAutoDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonAutoDown << kDownButtonShift); }
+ bool downButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kDownButtonShift)) != 0; }
+
+ bool rightButtonDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonDown << kRightButtonShift); }
+ bool rightButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonAutoDown << kRightButtonShift); }
+ bool rightButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightButtonShift)) != 0; }
+
+ bool leftFireButtonDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonDown << kLeftFireButtonShift); }
+ bool leftFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonAutoDown << kLeftFireButtonShift); }
+ bool leftFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftFireButtonShift)) != 0; }
+
+ bool rightFireButtonDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonDown << kRightFireButtonShift); }
+ bool rightFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonAutoDown << kRightFireButtonShift); }
+ bool rightFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightFireButtonShift)) != 0; }
+
+ bool oneButtonDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonDown << kOneButtonShift); }
+ bool oneButtonAutoDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonAutoDown << kOneButtonShift); }
+ bool oneButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kOneButtonShift)) != 0; }
+
+ bool twoButtonDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonDown << kTwoButtonShift); }
+ bool twoButtonAutoDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonAutoDown << kTwoButtonShift); }
+ bool twoButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kTwoButtonShift)) != 0; }
+
+ bool threeButtonDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonDown << kThreeButtonShift); }
+ bool threeButtonAutoDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonAutoDown << kThreeButtonShift); }
+ bool threeButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kThreeButtonShift)) != 0; }
+
+ bool fourButtonDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonDown << kFourButtonShift); }
+ bool fourButtonAutoDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonAutoDown << kFourButtonShift); }
+ bool fourButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kFourButtonShift)) != 0; }
+
+ bool mod1ButtonDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonDown << kMod1ButtonShift); }
+ bool mod1ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonAutoDown << kMod1ButtonShift); }
+ bool mod1ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod1ButtonShift)) != 0; }
+
+ bool mod2ButtonDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonDown << kMod2ButtonShift); }
+ bool mod2ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonAutoDown << kMod2ButtonShift); }
+ bool mod2ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod2ButtonShift)) != 0; }
+
+ bool mod3ButtonDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonDown << kMod3ButtonShift); }
+ bool mod3ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonAutoDown << kMod3ButtonShift); }
+ bool mod3ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod3ButtonShift)) != 0; }
+
+ bool allAutoInput() const { return (_inputState & kAllAutoBits) != 0; }
+ bool anyDirectionInput() const { return (_inputState & kDirectionBits) != 0; }
+ bool anyButtonInput() const { return (_inputState & kButtonBits) != 0; }
+ bool anyInput() const { return _inputState != 0; }
+
+ void getInputLocation(Common::Point &where) const { where = _inputLocation; }
+
+ bool anyInputBitSet(const InputBits bits) const { return (_inputState & bits) != 0; }
+
+ bool isAltDown() const { return _altDown; }
+ bool isConsoleRequested() const { return _consoleRequested; }
+
+ void clearInput() {
+ _inputState = kAllUpBits;
+ _inputLocation.x = 0;
+ _inputLocation.y = 0;
+ _consoleRequested = false;
+ _altDown = false;
+ }
+
+protected:
+ void setInputBits(const InputBits state) { _inputState = state; }
+ void setInputLocation(const Common::Point &where) { _inputLocation = where; }
+ void setConsoleRequested(bool consoleRequested) { _consoleRequested = consoleRequested; }
+ void setAltDown(bool altDown) { _altDown = altDown; }
+
+ InputBits _inputState;
+ Common::Point _inputLocation;
+ bool _consoleRequested;
+ bool _altDown;
+};
+
+class InputHandler {
+public:
+ static InputHandler *setInputHandler(InputHandler*);
+ static InputHandler *getCurrentHandler() { return _inputHandler; }
+ static void pollForInput();
+ static void getInput(Input&, Hotspot*&);
+ static void readInputDevice(Input&);
+ static void invalHotspots() { _invalHotspots = true; }
+ static InputBits getCurrentFilter() { return _lastFilter; }
+
+ InputHandler(InputHandler*);
+ virtual ~InputHandler();
+
+ virtual void setNextHandler(InputHandler *nextHandler) { _nextHandler = nextHandler; }
+ virtual InputHandler *getNextHandler() { return _nextHandler; }
+
+ virtual void handleInput(const Input &, const Hotspot *);
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void activateHotspots();
+ virtual void updateCursor(const Common::Point, const Hotspot *);
+ virtual bool isClickInput(const Input &, const Hotspot *);
+ virtual bool wantsCursor();
+
+ virtual bool releaseInputFocus() { return true; }
+ virtual void grabInputFocus() {}
+
+ // This returns bits set for what kinds of input to accept.
+ virtual InputBits getInputFilter();
+
+ // This returns bits defining what input constitutes a "click."
+ virtual InputBits getClickFilter();
+
+ virtual void allowInput(const bool allow) { _allowInput = allow; }
+
+protected:
+ static InputHandler *_inputHandler;
+ static bool _invalHotspots;
+ static InputBits _lastFilter;
+
+ InputHandler *_nextHandler;
+ bool _allowInput;
+};
+
+
+/*
+
+ Tracker implements "dragging". A Tracker can receive a startTracking message,
+ which causes it to be the current tracker, as well as setting it up as the current
+ input handler. In addition, only one tracker can be tracking at a time, and no
+ other handler can be set up as the current handler until the track finishes. By
+ default, there is no next input handler for a Tracker, but this behavior can be
+ overridden if desired.
+
+*/
+
+class Tracker : public InputHandler {
+public:
+ Tracker() : InputHandler(0) {}
+ virtual ~Tracker() {}
+
+ virtual void handleInput(const Input &, const Hotspot *);
+ virtual bool stopTrackingInput(const Input &) { return false; }
+
+ virtual void startTracking(const Input &);
+ virtual void stopTracking(const Input &);
+ virtual void continueTracking(const Input &) {}
+
+ bool isTracking() { return this == _currentTracker; }
+ bool isClickInput(const Input &, const Hotspot *);
+
+ bool releaseInputFocus() { return !isTracking(); }
+
+protected:
+ static Tracker *_currentTracker;
+
+ InputHandler *_savedHandler;
+};
+
+class JMPPPInput {
+public:
+ static bool isMenuButtonPressInput(const Input &input) { return input.twoButtonDown(); }
+
+ static InputBits getClickInputFilter() { return kFilterTwoButton; }
+ static bool isClickInput(const Input &input) { return input.twoButtonDown(); }
+ static bool isDraggingInput(const Input &input) { return input.twoButtonAnyDown(); }
+ static bool isPressingInput(const Input &input) { return input.twoButtonAnyDown(); }
+
+ static bool isRaiseInventoryInput(const Input &input) { return input.leftFireButtonDown(); }
+ static bool isRaiseBiochipsInput(const Input &input) { return input.rightFireButtonDown(); }
+ static InputBits getItemPanelsInputFilter() { return kFilterLeftFireButton | kFilterRightFireButton; }
+
+ static bool isToggleAIMiddleInput(const Input &input) { return input.threeButtonDown(); }
+
+ static bool isToggleInfoInput(const Input &input) { return input.fourButtonDown(); }
+
+ // Hmmmmm....
+ static bool isEasterEggModifierInput(const Input &input) { return input.isAltDown(); }
+
+ static bool isTogglePauseInput(const Input &input) { return input.mod3ButtonDown(); }
+};
+
+} // End of namespace Pegasus
+
+#define InputDevice (::Pegasus::InputDeviceManager::instance())
+
+#endif
diff --git a/engines/pegasus/interaction.h b/engines/pegasus/interaction.h
new file mode 100644
index 0000000000..293ee6be83
--- /dev/null
+++ b/engines/pegasus/interaction.h
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_INTERACTION_H
+#define PEGASUS_INTERACTION_H
+
+#include "pegasus/input.h"
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+static const InteractionID kNoInteractionID = -1;
+
+class Neighborhood;
+
+class GameInteraction : public IDObject, public InputHandler {
+public:
+ GameInteraction(const InteractionID id, Neighborhood *nextHandler) : IDObject(id), InputHandler((InputHandler *)nextHandler) {
+ _isInteracting = false;
+ _savedHandler = 0;
+ _owner = nextHandler;
+ }
+
+ // If the interaction is open (_isInteracting == true), it's too late to do anything
+ // about it here.
+ virtual ~GameInteraction() {}
+
+ // startInteraction and stopInteraction are called by the outside world to
+ // start and stop the interaction sequence.
+ // isInteracting returns a bool indicating whether or not the interaction
+ // is going.
+ void startInteraction() {
+ if (!isInteracting()) {
+ openInteraction();
+ initInteraction();
+ _isInteracting = true;
+ _savedHandler = InputHandler::setInputHandler(this);
+ }
+ }
+ void stopInteraction() {
+ if (isInteracting()) {
+ closeInteraction();
+ _isInteracting = false;
+ if (InputHandler::_inputHandler == this)
+ InputHandler::setInputHandler(_savedHandler);
+ }
+ }
+ void startOverInteraction() {
+ if (isInteracting())
+ resetInteraction();
+ }
+ bool isInteracting() const { return _isInteracting; }
+ Neighborhood *getOwner() const { return _owner; }
+
+ virtual Common::String getBriefingMovie() { return ""; }
+ virtual Common::String getEnvScanMovie() { return ""; }
+ virtual long getNumHints() { return 0; }
+ virtual Common::String getHintMovie(uint) { return ""; }
+ virtual bool canSolve() { return false; }
+
+ virtual void setSoundFXLevel(const uint16) {}
+ virtual void setAmbienceLevel(const uint16) {}
+
+ virtual void doSolve() {}
+
+protected:
+ // Subclasses override openInteraction and closeInteraction to perform
+ // specific initialization and cleanup. Override resetInteraction to
+ // "start the interaction over." resetInteraction is called only when
+ // the interaction is already open.
+ // These functions are only called in pairs, never two opens or closes
+ // in a row.
+ virtual void openInteraction() {}
+ virtual void initInteraction() {}
+ virtual void closeInteraction() {}
+ virtual void resetInteraction() {}
+
+ InputHandler *_savedHandler;
+ Neighborhood *_owner;
+
+private:
+ // Private so that only StartInteraction and StopInteraction can touch it.
+ bool _isInteracting;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/interface.cpp b/engines/pegasus/interface.cpp
new file mode 100644
index 0000000000..d9d3865192
--- /dev/null
+++ b/engines/pegasus/interface.cpp
@@ -0,0 +1,667 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/compass.h"
+#include "pegasus/energymonitor.h"
+#include "pegasus/interface.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/biochipitem.h"
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+Interface *g_interface = 0;
+
+Interface::Interface() : InputHandler(0), _interfaceNotification(kInterfaceNotificationID, (NotificationManager *)((PegasusEngine *)g_engine)),
+ _currentItemSpot(kCurrentItemSpotID), _currentBiochipSpot(kCurrentBiochipSpotID),
+ _background1(kInterface1ID), _background2(kInterface2ID), _background3(kInterface3ID),
+ _background4(kInterface4ID), _datePicture(kDateID), _inventoryPush(kInventoryPushID),
+ _inventoryLid(kInventoryLidID, kNoDisplayElement),
+ _inventoryPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getItemsInventory()),
+ _biochipPush(kBiochipPushID), _biochipLid(kBiochipLidID, kNoDisplayElement),
+ _biochipPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getBiochipsInventory()) {
+ g_energyMonitor = 0;
+ _previousHandler = 0;
+ _inventoryRaised = false;
+ _biochipRaised = false;
+ _playingEndMessage = false;
+ g_interface = this;
+}
+
+Interface::~Interface() {
+ throwAwayInterface();
+ g_interface = 0;
+}
+
+void Interface::throwAwayInterface() {
+ g_allHotspots.removeOneHotspot(kCurrentItemSpotID);
+ g_allHotspots.removeOneHotspot(kCurrentBiochipSpotID);
+
+ throwAwayBackground();
+ throwAwayDateMonitor();
+ throwAwayEnergyMonitor();
+ throwAwayAIArea();
+ throwAwayCompass();
+ throwAwayNotifications();
+ throwAwayInventoryPanel();
+ throwAwayBiochipPanel();
+}
+
+void Interface::validateBackground() {
+ if (!_background1.isSurfaceValid()) {
+ _background1.initFromPICTFile("Images/Interface/3DInterface Left");
+ _background2.initFromPICTFile("Images/Interface/3DInterface Top");
+ _background3.initFromPICTFile("Images/Interface/3DInterface Right");
+ _background4.initFromPICTFile("Images/Interface/3DInterface Bottom");
+
+ _background1.setDisplayOrder(kBackground1Order);
+ _background1.startDisplaying();
+ _background1.moveElementTo(kBackground1Left, kBackground1Top);
+
+ _background2.setDisplayOrder(kBackground2Order);
+ _background2.startDisplaying();
+ _background2.moveElementTo(kBackground2Left, kBackground2Top);
+
+ _background3.setDisplayOrder(kBackground2Order);
+ _background3.startDisplaying();
+ _background3.moveElementTo(kBackground3Left, kBackground3Top);
+
+ _background4.setDisplayOrder(kBackground4Order);
+ _background4.startDisplaying();
+ _background4.moveElementTo(kBackground4Left, kBackground4Top);
+
+ _background1.show();
+ _background2.show();
+ _background3.show();
+ _background4.show();
+ }
+}
+
+void Interface::throwAwayBackground() {
+ _background1.stopDisplaying();
+ _background1.deallocateSurface();
+ _background2.stopDisplaying();
+ _background2.deallocateSurface();
+ _background3.stopDisplaying();
+ _background3.deallocateSurface();
+ _background4.stopDisplaying();
+ _background4.deallocateSurface();
+}
+
+void Interface::validateDateMonitor() {
+ if (!_datePicture.isSurfaceValid()) {
+ _datePicture.setDisplayOrder(kDateOrder);
+ _datePicture.startDisplaying();
+ _datePicture.moveElementTo(kDateLeft, kDateTop);
+ _datePicture.show();
+ }
+}
+
+void Interface::throwAwayDateMonitor() {
+ _datePicture.stopDisplaying();
+ _datePicture.deallocateSurface();
+}
+
+void Interface::setDate(const uint16 dateResID) {
+ validateDateMonitor();
+ _datePicture.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, dateResID);
+ _datePicture.triggerRedraw();
+}
+
+void Interface::validateCompass() {
+ if (!g_compass) {
+ new Compass();
+ g_compass->initCompass();
+ g_compass->setDisplayOrder(kCompassOrder);
+ g_compass->startDisplaying();
+ g_compass->moveElementTo(kCompassLeft, kCompassTop);
+ g_compass->show();
+ }
+}
+
+void Interface::throwAwayCompass() {
+ delete g_compass;
+}
+
+void Interface::validateNotifications() {
+ _interfaceNotification.notifyMe(this, kInterfaceNotificationFlags, kInterfaceNotificationFlags);
+ _inventoryLidCallBack.setNotification(&_interfaceNotification);
+ _inventoryPushCallBack.setNotification(&_interfaceNotification);
+ _biochipLidCallBack.setNotification(&_interfaceNotification);
+ _biochipPushCallBack.setNotification(&_interfaceNotification);
+}
+
+void Interface::throwAwayNotifications() {
+ _interfaceNotification.cancelNotification(this);
+}
+
+void Interface::validateAIArea() {
+ if (!g_AIArea) {
+ new AIArea((InputHandler *)((PegasusEngine *)g_engine));
+ if (g_AIArea)
+ g_AIArea->initAIArea();
+ }
+}
+
+void Interface::throwAwayAIArea() {
+ delete g_AIArea;
+}
+
+void Interface::validateInventoryPanel() {
+ if (!_inventoryPanel.isSurfaceValid()) {
+ _inventoryPanel.initInventoryImage(&_inventoryPush);
+ _inventoryPanel.moveElementTo(kInventoryPushLeft, kInventoryPushTop);
+ _inventoryPush.setSlideDirection(kSlideUpMask);
+ _inventoryPush.setInAndOutElements(&_inventoryPanel, 0);
+ _inventoryPush.setDisplayOrder(kInventoryPushOrder);
+ _inventoryPush.startDisplaying();
+
+ _inventoryLid.useFileName("Images/Lids/Inventory Lid Sequence");
+ _inventoryLid.useTransparent(true);
+ _inventoryLid.openFrameSequence();
+ _inventoryLid.moveElementTo(kInventoryLidLeft, kInventoryLidTop);
+ _inventoryLid.setDisplayOrder(kInventoryLidOrder);
+ _inventoryLid.startDisplaying();
+
+ _inventoryPushCallBack.initCallBack(&_inventoryPush, kCallBackAtExtremes);
+ _inventoryLidCallBack.initCallBack(&_inventoryLid, kCallBackAtExtremes);
+
+ _inventoryUp = false;
+ _inventoryRaised = false;
+
+ Item *item = getCurrentInventoryItem();
+ if (item)
+ item->select();
+ }
+}
+
+void Interface::throwAwayInventoryPanel() {
+ _inventoryPanel.stopDisplaying();
+ _inventoryPanel.throwAwayInventoryImage();
+ _inventoryPush.stopDisplaying();
+ _inventoryLid.stopDisplaying();
+ _inventoryLid.closeFrameSequence();
+ _inventoryPushCallBack.releaseCallBack();
+ _inventoryLidCallBack.releaseCallBack();
+
+ Item *item = getCurrentInventoryItem();
+ if (item)
+ item->deselect();
+
+ _inventoryUp = false;
+ _inventoryRaised = false;
+}
+
+void Interface::validateBiochipPanel() {
+ if (!_biochipPanel.isSurfaceValid()) {
+ _biochipPanel.initInventoryImage(&_biochipPush);
+ _biochipPanel.moveElementTo(kBiochipPushLeft, kBiochipPushTop);
+ _biochipPush.setSlideDirection(kSlideUpMask);
+ _biochipPush.setInAndOutElements(&_biochipPanel, 0);
+ _biochipPush.setDisplayOrder(kBiochipPushOrder);
+ _biochipPush.startDisplaying();
+
+ _biochipLid.useFileName("Images/Lids/Biochip Lid Sequence");
+ _biochipLid.useTransparent(true);
+ _biochipLid.openFrameSequence();
+ _biochipLid.moveElementTo(kBiochipLidLeft, kBiochipLidTop);
+ _biochipLid.setDisplayOrder(kBiochipLidOrder);
+ _biochipLid.startDisplaying();
+
+ _biochipPushCallBack.initCallBack(&_biochipPush, kCallBackAtExtremes);
+ _biochipLidCallBack.initCallBack(&_biochipLid, kCallBackAtExtremes);
+
+ _biochipUp = false;
+ _biochipRaised = false;
+
+ Item *item = getCurrentBiochip();
+ if (item)
+ item->select();
+ }
+}
+
+void Interface::throwAwayBiochipPanel() {
+ _biochipPanel.stopDisplaying();
+ _biochipPanel.throwAwayInventoryImage();
+ _biochipPush.stopDisplaying();
+ _biochipLid.stopDisplaying();
+ _biochipLid.closeFrameSequence();
+ _biochipPushCallBack.releaseCallBack();
+ _biochipLidCallBack.releaseCallBack();
+
+ Item *item = getCurrentBiochip();
+ if (item)
+ item->deselect();
+
+ _biochipUp = false;
+ _biochipRaised = false;
+}
+
+void Interface::validateEnergyMonitor() {
+ if (!g_energyMonitor)
+ new EnergyMonitor();
+}
+
+void Interface::throwAwayEnergyMonitor() {
+ delete g_energyMonitor;
+}
+
+void Interface::createInterface() {
+ validateBackground();
+ validateDateMonitor();
+ validateCompass();
+ validateNotifications();
+ validateAIArea();
+ validateBiochipPanel();
+ validateInventoryPanel();
+ validateEnergyMonitor();
+
+ if (!g_allHotspots.findHotspotByID(kCurrentItemSpotID)) {
+ _currentItemSpot.setArea(Common::Rect(76, 334, 172, 430));
+ _currentItemSpot.setHotspotFlags(kShellSpotFlag);
+ _currentItemSpot.setActive();
+ g_allHotspots.push_back(&_currentItemSpot);
+ }
+
+ if (!g_allHotspots.findHotspotByID(kCurrentBiochipSpotID)) {
+ _currentBiochipSpot.setArea(Common::Rect(364, 334, 460, 430));
+ _currentBiochipSpot.setHotspotFlags(kShellSpotFlag);
+ _currentBiochipSpot.setActive();
+ g_allHotspots.push_back(&_currentBiochipSpot);
+ }
+}
+
+InventoryResult Interface::addInventoryItem(InventoryItem *item) {
+ return _inventoryPanel.addInventoryItem(item);
+}
+
+InventoryResult Interface::removeInventoryItem(InventoryItem *item) {
+ return _inventoryPanel.removeInventoryItem(item);
+}
+
+void Interface::removeAllItemsFromInventory() {
+ _inventoryPanel.removeAllItems();
+}
+
+InventoryItem *Interface::getCurrentInventoryItem() {
+ return (InventoryItem *)_inventoryPanel.getCurrentItem();
+}
+
+void Interface::setCurrentInventoryItem(InventoryItem *item) {
+ setCurrentInventoryItemID(item->getObjectID());
+}
+
+void Interface::setCurrentInventoryItemID(ItemID id) {
+ _inventoryPanel.setCurrentItemID(id);
+}
+
+InventoryResult Interface::addBiochip(BiochipItem *item) {
+ return _biochipPanel.addInventoryItem(item);
+}
+
+void Interface::removeAllItemsFromBiochips() {
+ _biochipPanel.removeAllItems();
+}
+
+BiochipItem *Interface::getCurrentBiochip() {
+ return (BiochipItem *)_biochipPanel.getCurrentItem();
+}
+
+void Interface::setCurrentBiochip(BiochipItem *item) {
+ setCurrentBiochipID(item->getObjectID());
+}
+
+void Interface::setCurrentBiochipID(ItemID id) {
+ _biochipPanel.setCurrentItemID(id);
+}
+
+void Interface::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ if (notification == &_interfaceNotification) {
+ switch (flags) {
+ case kInventoryLidOpenFlag:
+ inventoryLidOpen(true);
+ break;
+ case kInventoryLidClosedFlag:
+ inventoryLidClosed();
+ break;
+ case kInventoryDrawerUpFlag:
+ inventoryDrawerUp();
+ break;
+ case kInventoryDrawerDownFlag:
+ inventoryDrawerDown(true);
+ break;
+ case kBiochipLidOpenFlag:
+ biochipLidOpen(true);
+ break;
+ case kBiochipLidClosedFlag:
+ biochipLidClosed();
+ break;
+ case kBiochipDrawerUpFlag:
+ biochipDrawerUp();
+ break;
+ case kBiochipDrawerDownFlag:
+ biochipDrawerDown(true);
+ break;
+ }
+ }
+}
+
+void Interface::raiseInventoryDrawer(const bool doCallBacks) {
+ if (!_biochipUp)
+ _previousHandler = InputHandler::getCurrentHandler();
+
+ InputHandler::setInputHandler(&_inventoryPanel);
+ _inventoryUp = true;
+ _inventoryPanel.activateInventoryPicture();
+
+ if (doCallBacks) {
+ _inventoryLidCallBack.setCallBackFlag(kInventoryLidOpenFlag);
+ _inventoryLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ _inventoryLid.show();
+ _inventoryPush.show();
+ _inventoryLid.start();
+}
+
+void Interface::playEndMessage() {
+ raiseInventoryDrawerForMessage();
+ _playingEndMessage = true;
+ _inventoryPanel.playEndMessage(&_inventoryPush);
+ lowerInventoryDrawerForMessage();
+ _playingEndMessage = false;
+}
+
+void Interface::raiseInventoryDrawerForMessage() {
+ _inventoryPanel.disableLooping();
+ raiseInventoryDrawerSync();
+}
+
+void Interface::lowerInventoryDrawerForMessage() {
+ lowerInventoryDrawerSync();
+}
+
+void Interface::inventoryLidOpen(const bool doCallBacks) {
+ _inventoryLid.stop();
+
+ if (doCallBacks) {
+ _inventoryPushCallBack.setCallBackFlag(kInventoryDrawerUpFlag);
+ _inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ FaderMoveSpec moveSpec;
+ moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000);
+ _inventoryPush.startFader(moveSpec);
+}
+
+void Interface::inventoryDrawerUp() {
+ _inventoryPush.stopFader();
+ _inventoryPanel.panelUp();
+ _inventoryRaised = true;
+}
+
+bool Interface::isInventoryUp() {
+ return _inventoryRaised;
+}
+
+bool Interface::isInventoryDown() {
+ return !_inventoryUp;
+}
+
+void Interface::lowerInventoryDrawer(const bool doCallBacks) {
+ if (_inventoryRaised) {
+ _inventoryRaised = false;
+
+ if (!_playingEndMessage)
+ _inventoryPanel.deactivateInventoryPicture();
+
+ if (doCallBacks) {
+ _inventoryPushCallBack.setCallBackFlag(kInventoryDrawerDownFlag);
+ _inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ FaderMoveSpec moveSpec;
+ moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 15, 0);
+ _inventoryPush.startFader(moveSpec);
+ }
+}
+
+void Interface::inventoryDrawerDown(const bool doCallBacks) {
+ _inventoryPush.stopFader();
+
+ if (doCallBacks) {
+ _inventoryLidCallBack.setCallBackFlag(kInventoryLidClosedFlag);
+ _inventoryLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0);
+ }
+
+ _inventoryLid.setRate(-1);
+}
+
+void Interface::inventoryLidClosed() {
+ _inventoryLid.stop();
+
+ if (!_biochipUp)
+ InputHandler::setInputHandler(_previousHandler);
+
+ _inventoryLid.hide();
+ _inventoryPush.hide();
+ _inventoryUp = false;
+}
+
+void Interface::raiseBiochipDrawer(const bool doCallBacks) {
+ if (!_inventoryUp)
+ _previousHandler = InputHandler::getCurrentHandler();
+
+ InputHandler::setInputHandler(&_biochipPanel);
+ _biochipUp = true;
+ _biochipPanel.activateInventoryPicture();
+
+ if (doCallBacks) {
+ _biochipLidCallBack.setCallBackFlag(kBiochipLidOpenFlag);
+ _biochipLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ _biochipLid.show();
+ _biochipPush.show();
+ _biochipLid.start();
+}
+
+void Interface::biochipLidOpen(const bool doCallBacks) {
+ _biochipLid.stop();
+
+ if (doCallBacks) {
+ _biochipPushCallBack.setCallBackFlag(kBiochipDrawerUpFlag);
+ _biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ FaderMoveSpec moveSpec;
+ moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 9, 1000);
+ _biochipPush.startFader(moveSpec);
+}
+
+void Interface::biochipDrawerUp() {
+ _biochipPush.stopFader();
+ _biochipPanel.panelUp();
+ _biochipRaised = true;
+}
+
+void Interface::lowerBiochipDrawer(const bool doCallBacks) {
+ if (_biochipRaised) {
+ _biochipRaised = false;
+ _biochipPanel.deactivateInventoryPicture();
+
+ if (doCallBacks) {
+ _biochipPushCallBack.setCallBackFlag(kBiochipDrawerDownFlag);
+ _biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ FaderMoveSpec moveSpec;
+ moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 9, 0);
+ _biochipPush.startFader(moveSpec);
+ }
+}
+
+void Interface::biochipDrawerDown(const bool doCallBacks) {
+ _biochipPush.stopFader();
+
+ if (doCallBacks) {
+ _biochipLidCallBack.setCallBackFlag(kBiochipLidClosedFlag);
+ _biochipLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0);
+ }
+
+ _biochipLid.setRate(-1);
+}
+
+void Interface::biochipLidClosed() {
+ _biochipLid.stop();
+
+ if (!_inventoryUp)
+ InputHandler::setInputHandler(_previousHandler);
+
+ _biochipLid.hide();
+ _biochipPush.hide();
+ _biochipUp = false;
+}
+
+void Interface::calibrateCompass() {
+ uint32 currentValue = g_compass->getFaderValue();
+ FaderMoveSpec compassMove;
+ compassMove.makeTwoKnotFaderSpec(15, 0, currentValue, 30, currentValue + 360);
+
+ g_compass->startFader(compassMove);
+
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ while (g_compass->isFading()) {
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ g_compass->setFaderValue(currentValue);
+}
+
+void Interface::calibrateEnergyBar() {
+ g_energyMonitor->calibrateEnergyBar();
+}
+
+void Interface::raiseInventoryDrawerSync() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ raiseInventoryDrawer(false);
+
+ while (_inventoryLid.isRunning()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ inventoryLidOpen(false);
+
+ while (_inventoryPush.isFading()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ inventoryDrawerUp();
+}
+
+void Interface::lowerInventoryDrawerSync() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ lowerInventoryDrawer(false);
+
+ while (_inventoryPush.isFading()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ inventoryDrawerDown(false);
+
+ while (_inventoryLid.isRunning()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ inventoryLidClosed();
+}
+
+void Interface::raiseBiochipDrawerSync() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ raiseBiochipDrawer(false);
+
+ while (_biochipLid.isRunning()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ biochipLidOpen(false);
+
+ while (_biochipPush.isFading()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ biochipDrawerUp();
+}
+
+void Interface::lowerBiochipDrawerSync() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ lowerBiochipDrawer(false);
+
+ while (_biochipPush.isFading()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ biochipDrawerDown(false);
+
+ while (_biochipLid.isRunning()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ vm->refreshDisplay();
+ biochipLidClosed();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/interface.h b/engines/pegasus/interface.h
new file mode 100644
index 0000000000..a65d9a595a
--- /dev/null
+++ b/engines/pegasus/interface.h
@@ -0,0 +1,148 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_INTERFACE_H
+#define PEGASUS_INTERFACE_H
+
+#include "pegasus/hotspot.h"
+#include "pegasus/input.h"
+#include "pegasus/notification.h"
+#include "pegasus/surface.h"
+#include "pegasus/transition.h"
+#include "pegasus/items/inventorypicture.h"
+
+namespace Pegasus {
+
+class BiochipItem;
+class InventoryItem;
+
+class Interface : public InputHandler, public NotificationReceiver {
+public:
+ Interface();
+ virtual ~Interface();
+
+ void createInterface();
+
+ // Recalibration functions...
+ void calibrateCompass();
+ void calibrateEnergyBar();
+ void raiseInventoryDrawerSync();
+ void lowerInventoryDrawerSync();
+ void raiseBiochipDrawerSync();
+ void lowerBiochipDrawerSync();
+
+ void raiseInventoryDrawer(const bool doCallBacks = true);
+ void raiseBiochipDrawer(const bool doCallBacks = true);
+ void lowerInventoryDrawer(const bool doCallBacks = true);
+ void lowerBiochipDrawer(const bool doCallBacks = true);
+
+ void raiseInventoryDrawerForMessage();
+ void lowerInventoryDrawerForMessage();
+ bool isInventoryUp();
+ bool isInventoryDown();
+
+ InventoryResult addInventoryItem(InventoryItem *);
+ InventoryResult removeInventoryItem(InventoryItem *);
+ void removeAllItemsFromInventory();
+ InventoryItem *getCurrentInventoryItem();
+ void setCurrentInventoryItem(InventoryItem *);
+ void setCurrentInventoryItemID(ItemID);
+ InventoryResult addBiochip(BiochipItem *);
+ void removeAllItemsFromBiochips();
+ BiochipItem *getCurrentBiochip();
+ void setCurrentBiochip(BiochipItem *);
+ void setCurrentBiochipID(ItemID);
+
+ void setDate(const uint16);
+
+ void playEndMessage();
+
+ void throwAwayInterface();
+
+protected:
+ void validateBackground();
+ void validateDateMonitor();
+ void validateCompass();
+ void validateNotifications();
+ void validateAIArea();
+ void validateInventoryPanel();
+ void validateBiochipPanel();
+ void validateEnergyMonitor();
+
+ void throwAwayBackground();
+ void throwAwayDateMonitor();
+ void throwAwayCompass();
+ void throwAwayNotifications();
+ void throwAwayAIArea();
+ void throwAwayInventoryPanel();
+ void throwAwayBiochipPanel();
+ void throwAwayEnergyMonitor();
+
+ void receiveNotification(Notification *, const NotificationFlags);
+ void inventoryLidOpen(const bool doCallBacks);
+ void inventoryLidClosed();
+ void inventoryDrawerUp();
+ void inventoryDrawerDown(const bool doCallBacks);
+ void biochipLidOpen(const bool doCallBacks);
+ void biochipLidClosed();
+ void biochipDrawerUp();
+ void biochipDrawerDown(const bool doCallBacks);
+
+ Picture _background1;
+ Picture _background2;
+ Picture _background3;
+ Picture _background4;
+
+ Picture _datePicture;
+
+ InputHandler *_previousHandler;
+
+ Push _inventoryPush;
+ SpriteSequence _inventoryLid;
+ NotificationCallBack _inventoryPushCallBack;
+ NotificationCallBack _inventoryLidCallBack;
+ InventoryItemsPicture _inventoryPanel;
+ bool _inventoryUp, _inventoryRaised;
+
+ Push _biochipPush;
+ SpriteSequence _biochipLid;
+ NotificationCallBack _biochipPushCallBack;
+ NotificationCallBack _biochipLidCallBack;
+ BiochipPicture _biochipPanel;
+ bool _biochipUp, _biochipRaised;
+
+ Hotspot _currentItemSpot;
+ Hotspot _currentBiochipSpot;
+
+ Notification _interfaceNotification;
+
+ bool _playingEndMessage;
+};
+
+extern Interface *g_interface;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/autodragger.cpp b/engines/pegasus/items/autodragger.cpp
new file mode 100644
index 0000000000..40bad14a89
--- /dev/null
+++ b/engines/pegasus/items/autodragger.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/elements.h"
+#include "pegasus/items/autodragger.h"
+
+namespace Pegasus {
+
+AutoDragger::AutoDragger() {
+ _draggingElement = NULL;
+ _lastTime = 0;
+ initCallBack(this, kCallBackAtExtremes);
+}
+
+void AutoDragger::autoDrag(DisplayElement *dragElement, const Common::Point &startPoint, const Common::Point &stopPoint,
+ TimeValue dragTime, TimeScale dragScale) {
+ _draggingElement = dragElement;
+
+ if (_draggingElement) {
+ _startLocation = startPoint;
+ _stopLocation = stopPoint;
+ _lastTime = 0;
+ _done = false;
+ _draggingElement->moveElementTo(_startLocation.x, _startLocation.y);
+ setScale(dragScale);
+ setSegment(0, dragTime);
+ setTime(0);
+ scheduleCallBack(kTriggerAtStop, 0, 0);
+ startIdling();
+ start();
+ } else {
+ stopDragging();
+ }
+}
+
+void AutoDragger::stopDragging() {
+ cancelCallBack();
+ stopIdling();
+ _draggingElement = 0;
+ _startLocation = Common::Point();
+ _stopLocation = Common::Point();
+ _lastTime = 0;
+ _done = true;
+}
+
+bool AutoDragger::isDragging() {
+ return isIdling();
+}
+
+void AutoDragger::useIdleTime() {
+ TimeValue thisTime = getTime();
+
+ if (thisTime != _lastTime) {
+ int32 offsetX = (_stopLocation.x - _startLocation.x) * (int32)thisTime / (int32)getDuration();
+ int32 offsetY = (_stopLocation.y - _startLocation.y) * (int32)thisTime / (int32)getDuration();
+ _draggingElement->moveElementTo(_startLocation.x + offsetX, _startLocation.y + offsetY);
+ _lastTime = thisTime;
+ }
+
+ if (_done)
+ stopDragging();
+}
+
+void AutoDragger::callBack() {
+ if (isIdling())
+ _done = true;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/autodragger.h b/engines/pegasus/items/autodragger.h
new file mode 100644
index 0000000000..6783fdf9a3
--- /dev/null
+++ b/engines/pegasus/items/autodragger.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_AUTODRAGGER_H
+#define PEGASUS_ITEMS_AUTODRAGGER_H
+
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class DisplayElement;
+
+class AutoDragger : private Idler, private TimeBase, private TimeBaseCallBack {
+public:
+ AutoDragger();
+ virtual ~AutoDragger() {}
+
+ void autoDrag(DisplayElement *, const Common::Point &, const Common::Point &, TimeValue, TimeScale);
+ bool isDragging();
+ void stopDragging();
+
+protected:
+ void useIdleTime();
+ void callBack();
+
+ DisplayElement *_draggingElement;
+ Common::Point _startLocation, _stopLocation;
+ TimeValue _lastTime;
+ bool _done;
+};
+
+} // End of namespace Pegasus
+
+#endif
+
diff --git a/engines/pegasus/items/biochips/aichip.cpp b/engines/pegasus/items/biochips/aichip.cpp
new file mode 100644
index 0000000000..cbcfc363e8
--- /dev/null
+++ b/engines/pegasus/items/biochips/aichip.cpp
@@ -0,0 +1,279 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/aichip.h"
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+// indexed by [number of hints][number of solves (0, 1, or 2)][which button to highlight]
+static const ItemState s_highlightState[4][3][7] = {
+ {
+ {kAI000, -1, -1, -1, -1, kAI005, kAI006},
+ {kAI010, -1, -1, -1, -1, kAI015, kAI016},
+ {kAI020, -1, -1, -1, kAI024, -1, -1}
+ },
+ {
+ {kAI100, kAI101, -1, -1, -1, kAI105, kAI106},
+ {kAI110, kAI111, -1, -1, -1, kAI115, kAI116},
+ {kAI120, kAI121, -1, -1, kAI124, kAI125, kAI126}
+ },
+ {
+ {kAI200, kAI201, kAI202, -1, -1, kAI205, kAI206},
+ {kAI210, kAI211, kAI212, -1, -1, kAI215, kAI216},
+ {kAI220, kAI221, kAI222, -1, kAI224, kAI225, kAI226}
+ },
+ {
+ {kAI300, kAI301, kAI302, kAI303, -1, kAI305, kAI306},
+ {kAI310, kAI311, kAI312, kAI313, -1, kAI315, kAI316},
+ {kAI320, kAI321, kAI322, kAI323, kAI324, kAI325, kAI326}
+ }
+};
+
+AIChip *g_AIChip = 0;
+
+AIChip::AIChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ BiochipItem(id, neighborhood, room, direction), _briefingSpot(kAIBriefingSpotID), _scanSpot(kAIScanSpotID),
+ _hint1Spot(kAIHint1SpotID), _hint2Spot(kAIHint2SpotID), _hint3Spot(kAIHint3SpotID), _solveSpot(kAISolveSpotID) {
+ _briefingSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 10 + 81, kAIMiddleAreaTop + 27 + 31));
+ _briefingSpot.setHotspotFlags(kAIBiochipSpotFlag);
+ g_allHotspots.push_back(&_briefingSpot);
+
+ _scanSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 100, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 100 + 81, kAIMiddleAreaTop + 27 + 31));
+ _scanSpot.setHotspotFlags(kAIBiochipSpotFlag);
+ g_allHotspots.push_back(&_scanSpot);
+
+ _hint1Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 70, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 70 + 21, kAIMiddleAreaTop + 67 + 21));
+ _hint1Spot.setHotspotFlags(kAIBiochipSpotFlag);
+ g_allHotspots.push_back(&_hint1Spot);
+
+ _hint2Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 91, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 91 + 20, kAIMiddleAreaTop + 67 + 21));
+ _hint2Spot.setHotspotFlags(kAIBiochipSpotFlag);
+ g_allHotspots.push_back(&_hint2Spot);
+
+ _hint3Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 111, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 111 + 20, kAIMiddleAreaTop + 67 + 21));
+ _hint3Spot.setHotspotFlags(kAIBiochipSpotFlag);
+ g_allHotspots.push_back(&_hint3Spot);
+
+ _solveSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 131, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 131 + 50, kAIMiddleAreaTop + 67 + 21));
+ _solveSpot.setHotspotFlags(kAIBiochipSpotFlag);
+ g_allHotspots.push_back(&_solveSpot);
+
+ _playingMovie = false;
+ setItemState(kAI000);
+
+ g_AIChip = this;
+}
+
+AIChip::~AIChip() {
+ g_AIChip = NULL;
+
+ g_allHotspots.removeOneHotspot(kAIBriefingSpotID);
+ g_allHotspots.removeOneHotspot(kAIScanSpotID);
+ g_allHotspots.removeOneHotspot(kAIHint1SpotID);
+ g_allHotspots.removeOneHotspot(kAIHint2SpotID);
+ g_allHotspots.removeOneHotspot(kAIHint3SpotID);
+ g_allHotspots.removeOneHotspot(kAISolveSpotID);
+}
+
+void AIChip::select() {
+ BiochipItem::select();
+ setUpAIChip();
+}
+
+void AIChip::takeSharedArea() {
+ setUpAIChip();
+}
+
+void AIChip::setUpAIChip() {
+ if (!_playingMovie) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ uint numSolves;
+ if (GameState.getWalkthroughMode()) {
+ if (vm->canSolve())
+ numSolves = 2;
+ else
+ numSolves = 1;
+ } else {
+ numSolves = 0;
+ }
+
+ setItemState(s_highlightState[vm->getNumHints()][numSolves][0]);
+ }
+}
+
+// Only does something when there are hints or solves available.
+void AIChip::setUpAIChipRude() {
+ if (!_playingMovie) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ uint numSolves;
+ if (GameState.getWalkthroughMode()) {
+ if (vm->canSolve())
+ numSolves = 2;
+ else
+ numSolves = 1;
+ } else {
+ numSolves = 0;
+ }
+
+ uint numHints = vm->getNumHints();
+ if (numSolves == 2 || numHints != 0)
+ setItemState(s_highlightState[numHints][numSolves][0]);
+ }
+}
+
+void AIChip::activateAIHotspots() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+ _briefingSpot.setActive();
+ _scanSpot.setActive();
+
+ switch (vm->getNumHints()) {
+ case 3:
+ _hint3Spot.setActive();
+ // fall through
+ case 2:
+ _hint2Spot.setActive();
+ // fall through
+ case 1:
+ _hint1Spot.setActive();
+ break;
+ }
+
+ if (GameState.getWalkthroughMode() && vm->canSolve())
+ _solveSpot.setActive();
+}
+
+void AIChip::showBriefingClicked() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ _playingMovie = true;
+
+ uint numSolves;
+ if (GameState.getWalkthroughMode()) {
+ if (vm->canSolve())
+ numSolves = 2;
+ else
+ numSolves = 1;
+ } else {
+ numSolves = 0;
+ }
+
+ ItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIBriefingSpotID - kAIHint1SpotID + 1];
+ if (newState != -1)
+ setItemState(newState);
+}
+
+void AIChip::showEnvScanClicked() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ _playingMovie = true;
+
+ uint numSolves;
+ if (GameState.getWalkthroughMode()) {
+ if (vm->canSolve())
+ numSolves = 2;
+ else
+ numSolves = 1;
+ } else {
+ numSolves = 0;
+ }
+
+ ItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIScanSpotID - kAIHint1SpotID + 1];
+
+ if (newState != -1)
+ setItemState(newState);
+}
+
+void AIChip::clearClicked() {
+ _playingMovie = false;
+ setUpAIChip();
+}
+
+void AIChip::clickInAIHotspot(HotSpotID id) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ Common::String movieName;
+
+ switch (id) {
+ case kAIBriefingSpotID:
+ movieName = vm->getBriefingMovie();
+ break;
+ case kAIScanSpotID:
+ movieName = vm->getEnvScanMovie();
+ break;
+ case kAIHint1SpotID:
+ movieName = vm->getHintMovie(1);
+ break;
+ case kAIHint2SpotID:
+ movieName = vm->getHintMovie(2);
+ break;
+ case kAIHint3SpotID:
+ movieName = vm->getHintMovie(3);
+ break;
+ case kAISolveSpotID:
+ g_neighborhood->doSolve();
+ break;
+ }
+
+ ItemState state = getItemState();
+
+ if (!movieName.empty()) {
+ _playingMovie = true;
+
+ uint numSolves;
+ if (GameState.getWalkthroughMode()) {
+ if (vm->canSolve())
+ numSolves = 2;
+ else
+ numSolves = 1;
+ } else {
+ numSolves = 0;
+ }
+
+ ItemState newState = s_highlightState[vm->getNumHints()][numSolves][id - kAIHint1SpotID + 1];
+
+ if (newState != -1)
+ setItemState(newState);
+
+ if (g_AIArea) {
+ vm->prepareForAIHint(movieName);
+ g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kHintInterruption);
+ vm->cleanUpAfterAIHint(movieName);
+ }
+
+ if (newState != -1)
+ setItemState(state);
+
+ _playingMovie = false;
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/aichip.h b/engines/pegasus/items/biochips/aichip.h
new file mode 100644
index 0000000000..7a33953612
--- /dev/null
+++ b/engines/pegasus/items/biochips/aichip.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_AICHIP_H
+#define PEGASUS_ITEMS_BIOCHIPS_AICHIP_H
+
+#include "pegasus/hotspot.h"
+#include "pegasus/items/biochips/biochipitem.h"
+
+namespace Pegasus {
+
+class AIChip : public BiochipItem {
+public:
+ AIChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~AIChip();
+
+ void select();
+
+ void setUpAIChip();
+
+ // Called to set up the AI chip when the AI chip is the current chip but does not
+ // own the center area.
+ void setUpAIChipRude();
+ void activateAIHotspots();
+ void clickInAIHotspot(HotSpotID);
+
+ void takeSharedArea();
+
+ void showBriefingClicked();
+ void showEnvScanClicked();
+ void clearClicked();
+
+protected:
+ Hotspot _briefingSpot;
+ Hotspot _scanSpot;
+ Hotspot _hint1Spot;
+ Hotspot _hint2Spot;
+ Hotspot _hint3Spot;
+ Hotspot _solveSpot;
+ bool _playingMovie;
+};
+
+extern AIChip *g_AIChip;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/biochips/biochipitem.cpp b/engines/pegasus/items/biochips/biochipitem.cpp
new file mode 100644
index 0000000000..5686948937
--- /dev/null
+++ b/engines/pegasus/items/biochips/biochipitem.cpp
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#include "common/stream.h"
+
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/biochipitem.h"
+
+namespace Pegasus {
+
+BiochipItem::BiochipItem(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ Item(id, neighborhood, room, direction) {
+
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ Common::SeekableReadStream *biochipInfo = vm->_resFork->getResource(MKTAG('B', 'i', 'o', 'I'), kItemBaseResID + id);
+ if (biochipInfo) {
+ _biochipInfoPanelTime = biochipInfo->readUint32BE();
+ delete biochipInfo;
+ } else {
+ _biochipInfoPanelTime = 0;
+ }
+
+ Common::SeekableReadStream *rightInfo = vm->_resFork->getResource(MKTAG('R', 'g', 'h', 't'), kItemBaseResID + id);
+ if (!rightInfo)
+ error("Could not find right info for biochip %d", id);
+
+ _rightAreaInfo = readItemState(rightInfo);
+ delete rightInfo;
+
+ setItemState(kNormalItem);
+}
+
+BiochipItem::~BiochipItem() {
+ delete[] _rightAreaInfo.entries;
+}
+
+ItemType BiochipItem::getItemType() {
+ return kBiochipItemType;
+}
+
+TimeValue BiochipItem::getRightAreaTime() const {
+ if (!_rightAreaInfo.entries)
+ return 0xffffffff;
+
+ TimeValue time;
+ ItemState state;
+
+ findItemStateEntryByState(_rightAreaInfo, _itemState, time);
+ if (time == 0xffffffff)
+ getItemStateEntry(_rightAreaInfo, 0, state, time);
+
+ return time;
+}
+
+// Must affect images in right area.
+void BiochipItem::select() {
+ Item::select();
+
+ if (g_AIArea)
+ g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, getRightAreaTime());
+}
+
+void BiochipItem::deselect() {
+ Item::deselect();
+
+ if (g_AIArea)
+ g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, 0xffffffff);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/biochipitem.h b/engines/pegasus/items/biochips/biochipitem.h
new file mode 100644
index 0000000000..2039e80c6f
--- /dev/null
+++ b/engines/pegasus/items/biochips/biochipitem.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H
+#define PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H
+
+#include "pegasus/items/item.h"
+
+namespace Pegasus {
+
+class BiochipItem : public Item {
+public:
+ BiochipItem(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~BiochipItem();
+
+ virtual ItemType getItemType();
+
+ TimeValue getPanelTime() const { return _biochipInfoPanelTime; }
+ TimeValue getRightAreaTime() const;
+
+ // Must affect images in right area.
+ virtual void select();
+ virtual void deselect();
+
+protected:
+ TimeValue _biochipInfoPanelTime;
+ ItemStateInfo _rightAreaInfo;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/biochips/mapchip.cpp b/engines/pegasus/items/biochips/mapchip.cpp
new file mode 100644
index 0000000000..69050d5193
--- /dev/null
+++ b/engines/pegasus/items/biochips/mapchip.cpp
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/mapchip.h"
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+MapChip *g_map = 0;
+
+MapChip::MapChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ BiochipItem(id, neighborhood, room, direction) {
+ g_map = this;
+ setItemState(kMapUnavailable);
+}
+
+MapChip::~MapChip() {
+ g_map = 0;
+}
+
+void MapChip::writeToStream(Common::WriteStream *stream) {
+ return _image.writeToStream(stream);
+}
+
+void MapChip::readFromStream(Common::ReadStream *stream) {
+ return _image.readFromStream(stream);
+}
+
+void MapChip::select() {
+ BiochipItem::select();
+ moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ _image.show();
+}
+
+void MapChip::takeSharedArea() {
+ _image.show();
+}
+
+void MapChip::giveUpSharedArea() {
+ _image.hide();
+}
+
+void MapChip::deselect() {
+ BiochipItem::deselect();
+ _image.unloadImage();
+}
+
+void MapChip::moveToMapLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant dir) {
+ AirQuality airQuality;
+
+ if (g_neighborhood)
+ airQuality = g_neighborhood->getAirQuality(room);
+ else
+ airQuality = kAirQualityGood;
+
+ switch (neighborhood) {
+ case kMarsID:
+ if (airQuality == kAirQualityVacuum) {
+ if (room >= kMars35 && room <= kMars39) {
+ setItemState(kMapEngaged);
+ if (isSelected() && g_AIArea && g_AIArea->getMiddleAreaOwner() == kBiochipSignature)
+ _image.loadGearRoomIfNecessary();
+ } else {
+ setItemState(kMapEngaged);
+ if (isSelected() && g_AIArea && g_AIArea->getMiddleAreaOwner() == kBiochipSignature)
+ _image.loadMazeIfNecessary();
+ }
+
+ _image.moveToMapLocation(neighborhood, room, dir);
+ } else {
+ _image.unloadImage();
+ setItemState(kMapUnavailable);
+ }
+ break;
+ default:
+ _image.unloadImage();
+ setItemState(kMapUnavailable);
+ break;
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/mapchip.h b/engines/pegasus/items/biochips/mapchip.h
new file mode 100644
index 0000000000..6690090aa4
--- /dev/null
+++ b/engines/pegasus/items/biochips/mapchip.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_MAPCHIP_H
+#define PEGASUS_ITEMS_BIOCHIPS_MAPCHIP_H
+
+#include "pegasus/items/biochips/biochipitem.h"
+#include "pegasus/items/biochips/mapimage.h"
+
+namespace Common {
+ class ReadStream;
+ class WriteStream;
+}
+
+namespace Pegasus {
+
+class MapChip : public BiochipItem {
+public:
+ MapChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~MapChip();
+
+ void select();
+ void deselect();
+ void takeSharedArea();
+ void giveUpSharedArea();
+
+ void moveToMapLocation(const NeighborhoodID, const RoomID, const DirectionConstant);
+
+ void writeToStream(Common::WriteStream *);
+ void readFromStream(Common::ReadStream *);
+
+ bool beenToMaze() { return _image.anyFlagSet(); }
+
+protected:
+ MapImage _image;
+};
+
+extern MapChip *g_map;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/biochips/mapimage.cpp b/engines/pegasus/items/biochips/mapimage.cpp
new file mode 100644
index 0000000000..9f4170d063
--- /dev/null
+++ b/engines/pegasus/items/biochips/mapimage.cpp
@@ -0,0 +1,443 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/mapimage.h"
+
+namespace Pegasus {
+
+#define FLAG_TO_INDEX(flag) ((flag) >> 2)
+#define INDEX_TO_FLAG(index) ((index) << 2)
+
+#define ROOM_TO_INDEX(room) \
+ (((room) >= kMars35 && (room) <= kMars39) ? ((room) - kMars35) : \
+ (((room) == kMars60) ? (kMars39 - kMars35 + 1) : \
+ ((room) - kMarsMaze004 + kMars39 - kMars35 + 2)))
+
+#define INDEX_TO_ROOM(index) \
+ (((index) <= ROOM_TO_INDEX(kMars39)) ? \
+ (((index) - ROOM_TO_INDEX(kMars35)) + kMars35) : \
+ ((index) <= ROOM_TO_INDEX(kMars60,)) ? kMars60 : \
+ ((((index) - ROOM_TO_INDEX(kMarsMaze004))) + kMarsMaze004))
+
+#define ROOM_TO_FLAG(room, dir) (INDEX_TO_FLAG(ROOM_TO_INDEX(room)) | (dir))
+
+#define FLAG_TO_ROOM(flag) (INDEX_TO_ROOM(FLAG_TO_INDEX(flag)))
+
+#define FLAG_TO_DIRECTION(flag) ((flag) & 3)
+
+static const int kGearRoomFlagLow = ROOM_TO_FLAG(kMars35, kNorth);
+static const int kGearRoomFlagHigh = ROOM_TO_FLAG(kMars39, kWest);
+
+static const int kMazeFlagLow = ROOM_TO_FLAG(kMars60, kNorth);
+static const int kMazeFlagHigh = ROOM_TO_FLAG(kMarsMaze200, kWest);
+
+static const CoordType kGearRoomScreenOffsetX = 49;
+static const CoordType kGearRoomScreenOffsetY = 47;
+
+static const CoordType kGearRoomGridOriginX = 1;
+static const CoordType kGearRoomGridOriginY = 4;
+
+static const CoordType kMazeScreenOffsetX = 16;
+static const CoordType kMazeScreenOffsetY = 20;
+
+static const CoordType kMazeGridOriginX = 6;
+static const CoordType kMazeGridOriginY = 1;
+
+static const CoordType kGridWidth = 4;
+static const CoordType kGridHeight = 4;
+
+static const uint16 kMapOfMazePICTID = 906;
+static const uint16 kMapOfGearRoomPICTID = 907;
+
+static const int s_mapCoords[MapImage::kNumMappingRooms][2] = {
+ /* kMars35 */ { 0, 0 },
+ /* kMars36 */ { 1, 0 },
+ /* kMars37 */ { 2, 0 },
+ /* kMars38 */ { 3, 0 },
+ /* kMars39 */ { 4, 0 },
+ /* kMars60 */ { 19, 9 },
+ /* kMarsMaze004 */ { 18, 9 },
+ /* kMarsMaze005 */ { 18, 10 },
+ /* kMarsMaze006 */ { 17, 10 },
+ /* kMarsMaze007 */ { 16, 10 },
+ /* kMarsMaze008 */ { 15, 10 },
+ /* kMarsMaze009 */ { 14, 10 },
+ /* kMarsMaze010 */ { 14, 9 },
+ /* kMarsMaze011 */ { 14, 8 },
+ /* kMarsMaze012 */ { 14, 7 },
+ /* kMarsMaze015 */ { 16, 7 },
+ /* kMarsMaze016 */ { 14, 11 },
+ /* kMarsMaze017 */ { 14, 12 },
+ /* kMarsMaze018 */ { 15, 12 },
+ /* kMarsMaze019 */ { 16, 12 },
+ /* kMarsMaze020 */ { 16, 13 },
+ /* kMarsMaze021 */ { 16, 14 },
+ /* kMarsMaze022 */ { 16, 15 },
+ /* kMarsMaze023 */ { 17, 15 },
+ /* kMarsMaze024 */ { 18, 15 },
+ /* kMarsMaze025 */ { 18, 14 },
+ /* kMarsMaze026 */ { 18, 13 },
+ /* kMarsMaze027 */ { 18, 12 },
+ /* kMarsMaze028 */ { 18, 11 },
+ /* kMarsMaze031 */ { 19, 14 },
+ /* kMarsMaze032 */ { 20, 14 },
+ /* kMarsMaze033 */ { 20, 13 },
+ /* kMarsMaze034 */ { 20, 12 },
+ /* kMarsMaze035 */ { 20, 11 },
+ /* kMarsMaze036 */ { 21, 11 },
+ /* kMarsMaze037 */ { 15, 15 },
+ /* kMarsMaze038 */ { 14, 15 },
+ /* kMarsMaze039 */ { 13, 15 },
+ /* kMarsMaze042 */ { 10, 15 },
+ /* kMarsMaze043 */ { 9, 15 },
+ /* kMarsMaze044 */ { 8, 15 },
+ /* kMarsMaze045 */ { 7, 15 },
+ /* kMarsMaze046 */ { 6, 15 },
+ /* kMarsMaze047 */ { 5, 15 },
+ /* kMarsMaze049 */ { 13, 14 },
+ /* kMarsMaze050 */ { 12, 14 },
+ /* kMarsMaze051 */ { 11, 14 },
+ /* kMarsMaze052 */ { 10, 14 },
+ /* kMarsMaze053 */ { 10, 13 },
+ /* kMarsMaze054 */ { 9, 13 },
+ /* kMarsMaze055 */ { 8, 13 },
+ /* kMarsMaze056 */ { 8, 12 },
+ /* kMarsMaze057 */ { 7, 12 },
+ /* kMarsMaze058 */ { 12, 13 },
+ /* kMarsMaze059 */ { 12, 12 },
+ /* kMarsMaze060 */ { 12, 11 },
+ /* kMarsMaze061 */ { 12, 10 },
+ /* kMarsMaze063 */ { 12, 9 },
+ /* kMarsMaze064 */ { 12, 8 },
+ /* kMarsMaze065 */ { 12, 7 },
+ /* kMarsMaze066 */ { 13, 7 },
+ /* kMarsMaze067 */ { 15, 7 },
+ /* kMarsMaze068 */ { 17, 7 },
+ /* kMarsMaze069 */ { 18, 7 },
+ /* kMarsMaze070 */ { 19, 7 },
+ /* kMarsMaze071 */ { 20, 7 },
+ /* kMarsMaze072 */ { 20, 6 },
+ /* kMarsMaze074 */ { 20, 5 },
+ /* kMarsMaze076 */ { 20, 4 },
+ /* kMarsMaze078 */ { 20, 3 },
+ /* kMarsMaze079 */ { 20, 2 },
+ /* kMarsMaze081 */ { 20, 2 },
+ /* kMarsMaze083 */ { 20, 0 },
+ /* kMarsMaze084 */ { 19, 0 },
+ /* kMarsMaze085 */ { 18, 0 },
+ /* kMarsMaze086 */ { 17, 0 },
+ /* kMarsMaze087 */ { 16, 0 },
+ /* kMarsMaze088 */ { 15, 0 },
+ /* kMarsMaze089 */ { 14, 0 },
+ /* kMarsMaze090 */ { 13, 0 },
+ /* kMarsMaze091 */ { 12, 0 },
+ /* kMarsMaze092 */ { 11, 0 },
+ /* kMarsMaze093 */ { 10, 0 },
+ /* kMarsMaze098 */ { 10, 1 },
+ /* kMarsMaze099 */ { 8, 2 },
+ /* kMarsMaze100 */ { 9, 2 },
+ /* kMarsMaze101 */ { 10, 2 },
+ /* kMarsMaze104 */ { 13, 2 },
+ /* kMarsMaze105 */ { 13, 3 },
+ /* kMarsMaze106 */ { 13, 4 },
+ /* kMarsMaze107 */ { 13, 5 },
+ /* kMarsMaze108 */ { 14, 5 },
+ /* kMarsMaze111 */ { 15, 5 },
+ /* kMarsMaze113 */ { 16, 5 },
+ /* kMarsMaze114 */ { 17, 5 },
+ /* kMarsMaze115 */ { 18, 5 },
+ /* kMarsMaze116 */ { 18, 4 },
+ /* kMarsMaze117 */ { 18, 3 },
+ /* kMarsMaze118 */ { 19, 3 },
+ /* kMarsMaze119 */ { 18, 2 },
+ /* kMarsMaze120 */ { 17, 2 },
+ /* kMarsMaze121 */ { 16, 2 },
+ /* kMarsMaze122 */ { 15, 2 },
+ /* kMarsMaze123 */ { 15, 1 },
+ /* kMarsMaze124 */ { 12, 4 },
+ /* kMarsMaze125 */ { 11, 4 },
+ /* kMarsMaze126 */ { 10, 4 },
+ /* kMarsMaze127 */ { 10, 5 },
+ /* kMarsMaze128 */ { 10, 6 },
+ /* kMarsMaze129 */ { 9, 6 },
+ /* kMarsMaze130 */ { 8, 6 },
+ /* kMarsMaze131 */ { 7, 6 },
+ /* kMarsMaze132 */ { 7, 7 },
+ /* kMarsMaze133 */ { 7, 8 },
+ /* kMarsMaze136 */ { 7, 11 },
+ /* kMarsMaze137 */ { 6, 11 },
+ /* kMarsMaze138 */ { 5, 11 },
+ /* kMarsMaze139 */ { 5, 12 },
+ /* kMarsMaze140 */ { 4, 12 },
+ /* kMarsMaze141 */ { 5, 13 },
+ /* kMarsMaze142 */ { 5, 14 },
+ /* kMarsMaze143 */ { 4, 14 },
+ /* kMarsMaze144 */ { 3, 14 },
+ /* kMarsMaze145 */ { 3, 13 },
+ /* kMarsMaze146 */ { 2, 13 },
+ /* kMarsMaze147 */ { 1, 13 },
+ /* kMarsMaze148 */ { 1, 14 },
+ /* kMarsMaze149 */ { 1, 15 },
+ /* kMarsMaze152 */ { 1, 12 },
+ /* kMarsMaze153 */ { 1, 11 },
+ /* kMarsMaze154 */ { 1, 10 },
+ /* kMarsMaze155 */ { 1, 9 },
+ /* kMarsMaze156 */ { 1, 8 },
+ /* kMarsMaze157 */ { 2, 10 },
+ /* kMarsMaze159 */ { 2, 8 },
+ /* kMarsMaze160 */ { 2, 7 },
+ /* kMarsMaze161 */ { 2, 6 },
+ /* kMarsMaze162 */ { 3, 10 },
+ /* kMarsMaze163 */ { 3, 9 },
+ /* kMarsMaze164 */ { 3, 8 },
+ /* kMarsMaze165 */ { 4, 8 },
+ /* kMarsMaze166 */ { 5, 8 },
+ /* kMarsMaze167 */ { 6, 8 },
+ /* kMarsMaze168 */ { 3, 6 },
+ /* kMarsMaze169 */ { 4, 6 },
+ /* kMarsMaze170 */ { 5, 6 },
+ /* kMarsMaze171 */ { 5, 5 },
+ /* kMarsMaze172 */ { 5, 4 },
+ /* kMarsMaze173 */ { 4, 4 },
+ /* kMarsMaze174 */ { 3, 4 },
+ /* kMarsMaze175 */ { 3, 5 },
+ /* kMarsMaze177 */ { 8, 4 },
+ /* kMarsMaze178 */ { 8, 3 },
+ /* kMarsMaze179 */ { 7, 4 },
+ /* kMarsMaze180 */ { 6, 4 },
+ /* kMarsMaze181 */ { 6, 3 },
+ /* kMarsMaze182 */ { 6, 2 },
+ /* kMarsMaze183 */ { 6, 1 },
+ /* kMarsMaze184 */ { 6, 0 },
+ /* kMarsMaze187 */ { 3, 0 },
+ /* kMarsMaze188 */ { 2, 0 },
+ /* kMarsMaze189 */ { 1, 0 },
+ /* kMarsMaze190 */ { 1, 1 },
+ /* kMarsMaze191 */ { 1, 2 },
+ /* kMarsMaze192 */ { 5, 2 },
+ /* kMarsMaze193 */ { 4, 2 },
+ /* kMarsMaze194 */ { 3, 2 },
+ /* kMarsMaze195 */ { 3, 1 },
+ /* kMarsMaze198 */ { 1, 3 },
+ /* kMarsMaze199 */ { 1, 4 },
+ /* kMarsMaze200 */ { 0, 4 }
+};
+
+MapImage::MapImage() : DisplayElement(kNoDisplayElement) {
+ _whichArea = kMapNoArea;
+ setBounds(kAIMiddleAreaLeft, kAIMiddleAreaTop, kAIMiddleAreaLeft + kAIMiddleAreaWidth, kAIMiddleAreaTop + kAIMiddleAreaHeight);
+ setDisplayOrder(kAIMiddleAreaOrder + 10);
+ startDisplaying();
+
+ _darkGreen = g_system->getScreenFormat().RGBToColor(64, 150, 10);
+ _lightGreen = g_system->getScreenFormat().RGBToColor(102, 239, 0);
+}
+
+void MapImage::writeToStream(Common::WriteStream *stream) {
+ _mappedRooms.writeToStream(stream);
+}
+
+void MapImage::readFromStream(Common::ReadStream *stream) {
+ _mappedRooms.readFromStream(stream);
+}
+
+void MapImage::loadGearRoomIfNecessary() {
+ if (_whichArea != kMapGearRoom) {
+ _mapImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMapOfGearRoomPICTID);
+
+ Common::Rect bounds;
+ _mapImage.getSurfaceBounds(bounds);
+ _mapMask.allocateSurface(bounds);
+ _whichArea = kMapGearRoom;
+
+ GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx;
+ gfx->setCurSurface(_mapMask.getSurface());
+
+ gfx->getCurSurface()->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff));
+
+ for (int i = kGearRoomFlagLow; i <= kGearRoomFlagHigh; i++)
+ if (_mappedRooms.getFlag(i))
+ addFlagToMask(i);
+
+ gfx->setCurSurface(gfx->getWorkArea());
+ show();
+ }
+}
+
+void MapImage::loadMazeIfNecessary() {
+ if (_whichArea != kMapMaze) {
+ _mapImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMapOfMazePICTID);
+
+ Common::Rect bounds;
+ _mapImage.getSurfaceBounds(bounds);
+ _mapMask.allocateSurface(bounds);
+ _whichArea = kMapMaze;
+
+ GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx;
+ gfx->setCurSurface(_mapMask.getSurface());
+
+ gfx->getCurSurface()->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff));
+
+ for (int i = kMazeFlagLow; i <= kMazeFlagHigh; i++)
+ if (_mappedRooms.getFlag(i))
+ addFlagToMask(i);
+
+ gfx->setCurSurface(gfx->getWorkArea());
+ show();
+ }
+}
+
+void MapImage::unloadImage() {
+ _mapImage.deallocateSurface();
+ _mapMask.deallocateSurface();
+ hide();
+ _whichArea = kMapNoArea;
+}
+
+void MapImage::moveToMapLocation(const NeighborhoodID, const RoomID room, const DirectionConstant dir) {
+ GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx;
+
+ int flag = ROOM_TO_FLAG(room, dir);
+
+ if (!_mappedRooms.getFlag(flag)) {
+ _mappedRooms.setFlag(flag, true);
+
+ if (_mapMask.isSurfaceValid()) {
+ gfx->setCurSurface(_mapMask.getSurface());
+ addFlagToMask(flag);
+ gfx->setCurSurface(gfx->getWorkArea());
+ }
+ }
+
+ if (isDisplaying())
+ triggerRedraw();
+}
+
+void MapImage::addFlagToMask(const int flag) {
+ Common::Rect r1;
+ getRevealedRects(flag, r1);
+ ((PegasusEngine *)g_engine)->_gfx->getCurSurface()->fillRect(r1, g_system->getScreenFormat().RGBToColor(0, 0, 0));
+}
+
+// This function can even be sensitive to open doors.
+// clone2727 notices that it's not, though
+void MapImage::getRevealedRects(const uint32 flag, Common::Rect &r1) {
+ CoordType gridX, gridY;
+
+ switch (_whichArea) {
+ case kMapMaze:
+ gridX = kMazeGridOriginX;
+ gridY = kMazeGridOriginY;
+ break;
+ case kMapGearRoom:
+ gridX = kGearRoomGridOriginX;
+ gridY = kGearRoomGridOriginY;
+ break;
+ default:
+ return;
+ }
+
+ int index = FLAG_TO_INDEX(flag);
+ gridX += s_mapCoords[index][0] * kGridWidth;
+ gridY += s_mapCoords[index][1] * kGridHeight;
+
+ r1 = Common::Rect(gridX - 1, gridY - 1, gridX + kGridWidth + 1, gridY + kGridHeight + 1);
+}
+
+void MapImage::drawPlayer() {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface();
+
+ CoordType gridX, gridY;
+
+ switch (_whichArea) {
+ case kMapMaze:
+ gridX = _bounds.left + kMazeScreenOffsetX + kMazeGridOriginX;
+ gridY = _bounds.top + kMazeScreenOffsetY + kMazeGridOriginY;
+ break;
+ case kMapGearRoom:
+ gridX = _bounds.left + kGearRoomScreenOffsetX + kGearRoomGridOriginX;
+ gridY = _bounds.top + kGearRoomScreenOffsetY + kGearRoomGridOriginY;
+ break;
+ default:
+ return;
+ }
+
+ int index = ROOM_TO_INDEX(GameState.getCurrentRoom());
+ gridX += s_mapCoords[index][0] * kGridWidth;
+ gridY += s_mapCoords[index][1] * kGridHeight;
+
+ // This was intended to make little arrows
+ switch (GameState.getCurrentDirection()) {
+ case kNorth:
+ screen->drawLine(gridX + 1, gridY, gridX + 2, gridY, _darkGreen);
+ screen->drawLine(gridX, gridY + 1, gridX + 3, gridY + 1, _darkGreen);
+ screen->drawLine(gridX + 1, gridY + 1, gridX + 2, gridY + 1, _lightGreen);
+ screen->drawLine(gridX, gridY + 2, gridX + 3, gridY + 2, _lightGreen);
+ break;
+ case kSouth:
+ screen->drawLine(gridX + 1, gridY + 3, gridX + 2, gridY + 3, _darkGreen);
+ screen->drawLine(gridX, gridY + 2, gridX + 3, gridY + 2, _darkGreen);
+ screen->drawLine(gridX + 1, gridY + 2, gridX + 2, gridY + 2, _lightGreen);
+ screen->drawLine(gridX, gridY + 1, gridX + 3, gridY + 1, _lightGreen);
+ break;
+ case kEast:
+ screen->drawLine(gridX + 3, gridY + 1, gridX + 3, gridY + 2, _darkGreen);
+ screen->drawLine(gridX + 2, gridY, gridX + 2, gridY + 3, _darkGreen);
+ screen->drawLine(gridX + 2, gridY + 1, gridX + 2, gridY + 2, _lightGreen);
+ screen->drawLine(gridX + 1, gridY, gridX + 1, gridY + 3, _lightGreen);
+ break;
+ case kWest:
+ screen->drawLine(gridX, gridY + 1, gridX, gridY + 2, _darkGreen);
+ screen->drawLine(gridX + 1, gridY, gridX + 1, gridY + 3, _darkGreen);
+ screen->drawLine(gridX + 1, gridY + 1, gridX + 1, gridY + 2, _lightGreen);
+ screen->drawLine(gridX + 2, gridY, gridX + 2, gridY + 3, _lightGreen);
+ break;
+ }
+}
+
+void MapImage::draw(const Common::Rect &) {
+ Common::Rect r1;
+ _mapImage.getSurfaceBounds(r1);
+
+ Common::Rect r2 = r1;
+ switch (_whichArea) {
+ case kMapMaze:
+ r2.moveTo(_bounds.left + kMazeScreenOffsetX, _bounds.top + kMazeScreenOffsetY);
+ break;
+ case kMapGearRoom:
+ r2.moveTo(_bounds.left + kGearRoomScreenOffsetX, _bounds.top + kGearRoomScreenOffsetY);
+ break;
+ default:
+ return;
+ }
+
+ _mapImage.copyToCurrentPortMasked(r1, r2, &_mapMask);
+
+ drawPlayer();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/mapimage.h b/engines/pegasus/items/biochips/mapimage.h
new file mode 100644
index 0000000000..49ad9945ee
--- /dev/null
+++ b/engines/pegasus/items/biochips/mapimage.h
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_MAPIMAGE_H
+#define PEGASUS_ITEMS_BIOCHIPS_MAPIMAGE_H
+
+#include "pegasus/elements.h"
+#include "pegasus/surface.h"
+#include "pegasus/util.h"
+#include "pegasus/neighborhood/mars/constants.h"
+
+namespace Common {
+ class ReadStream;
+ class WriteStream;
+}
+
+namespace Pegasus {
+
+class MapImage : public DisplayElement {
+public:
+ MapImage();
+ virtual ~MapImage() {}
+
+ void writeToStream(Common::WriteStream *);
+ void readFromStream(Common::ReadStream *);
+
+ void loadGearRoomIfNecessary();
+ void loadMazeIfNecessary();
+ void unloadImage();
+ void moveToMapLocation(const NeighborhoodID, const RoomID, const DirectionConstant);
+
+ void draw(const Common::Rect &);
+
+ bool anyFlagSet() { return _mappedRooms.anyFlagSet(); }
+
+ static const uint32 kNumMappingRooms = (kMars39 - kMars35 + 1) + (kMars60 - kMars60 + 1) +
+ (kMarsMaze200 - kMarsMaze004 + 1);
+ static const uint32 kNumMappingFlags = kNumMappingRooms * 4;
+
+protected:
+ enum MapArea {
+ kMapNoArea,
+ kMapMaze,
+ kMapGearRoom
+ };
+
+ void addFlagToMask(const int flag);
+ void getRevealedRects(const uint32, Common::Rect &);
+ void drawPlayer();
+
+ MapArea _whichArea;
+
+ FlagsArray<byte, kNumMappingFlags> _mappedRooms;
+
+ uint32 _darkGreen, _lightGreen;
+
+ Surface _mapImage, _mapMask;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/biochips/opticalchip.cpp b/engines/pegasus/items/biochips/opticalchip.cpp
new file mode 100644
index 0000000000..7b8858edae
--- /dev/null
+++ b/engines/pegasus/items/biochips/opticalchip.cpp
@@ -0,0 +1,190 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/opticalchip.h"
+
+namespace Pegasus {
+
+OpticalChip *g_opticalChip = 0;
+
+OpticalChip::OpticalChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ BiochipItem(id, neighborhood, room, direction), _ariesHotspot(kAriesSpotID), _mercuryHotspot(kMercurySpotID),
+ _poseidonHotspot(kPoseidonSpotID) {
+ _ariesHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 27 + 20));
+ _ariesHotspot.setHotspotFlags(kOpticalBiochipSpotFlag);
+ g_allHotspots.push_back(&_ariesHotspot);
+
+ _mercuryHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 47, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 47 + 20));
+ _mercuryHotspot.setHotspotFlags(kOpticalBiochipSpotFlag);
+ g_allHotspots.push_back(&_mercuryHotspot);
+
+ _poseidonHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 67 + 20));
+ _poseidonHotspot.setHotspotFlags(kOpticalBiochipSpotFlag);
+ g_allHotspots.push_back(&_poseidonHotspot);
+
+ setItemState(kOptical000);
+
+ g_opticalChip = this;
+}
+
+OpticalChip::~OpticalChip() {
+ g_allHotspots.removeOneHotspot(kAriesSpotID);
+ g_allHotspots.removeOneHotspot(kMercurySpotID);
+ g_allHotspots.removeOneHotspot(kPoseidonSpotID);
+}
+
+void OpticalChip::writeToStream(Common::WriteStream *stream) {
+ BiochipItem::writeToStream(stream);
+ _opticalFlags.writeToStream(stream);
+}
+
+void OpticalChip::readFromStream(Common::ReadStream *stream) {
+ BiochipItem::readFromStream(stream);
+ _opticalFlags.readFromStream(stream);
+}
+
+void OpticalChip::addAries() {
+ _opticalFlags.setFlag(kOpticalAriesExposed, true);
+ setUpOpticalChip();
+}
+
+void OpticalChip::addMercury() {
+ _opticalFlags.setFlag(kOpticalMercuryExposed, true);
+ setUpOpticalChip();
+}
+
+void OpticalChip::addPoseidon() {
+ _opticalFlags.setFlag(kOpticalPoseidonExposed, true);
+ setUpOpticalChip();
+}
+
+void OpticalChip::setUpOpticalChip() {
+ if (_opticalFlags.getFlag(kOpticalAriesExposed)) {
+ if (_opticalFlags.getFlag(kOpticalMercuryExposed)) {
+ if (_opticalFlags.getFlag(kOpticalPoseidonExposed))
+ setItemState(kOptical111);
+ else
+ setItemState(kOptical011);
+ } else {
+ if (_opticalFlags.getFlag(kOpticalPoseidonExposed))
+ setItemState(kOptical101);
+ else
+ setItemState(kOptical001);
+ }
+ } else {
+ if (_opticalFlags.getFlag(kOpticalMercuryExposed)) {
+ if (_opticalFlags.getFlag(kOpticalPoseidonExposed))
+ setItemState(kOptical110);
+ else
+ setItemState(kOptical010);
+ } else {
+ if (_opticalFlags.getFlag(kOpticalPoseidonExposed))
+ setItemState(kOptical100);
+ else
+ setItemState(kOptical000);
+ }
+ }
+}
+
+void OpticalChip::activateOpticalHotspots() {
+ if (_opticalFlags.getFlag(kOpticalAriesExposed))
+ _ariesHotspot.setActive();
+ if (_opticalFlags.getFlag(kOpticalMercuryExposed))
+ _mercuryHotspot.setActive();
+ if (_opticalFlags.getFlag(kOpticalPoseidonExposed))
+ _poseidonHotspot.setActive();
+}
+
+void OpticalChip::clickInOpticalHotspot(HotSpotID id) {
+ playOpMemMovie(id);
+}
+
+void OpticalChip::playOpMemMovie(HotSpotID id) {
+ Common::String movieName;
+ switch (id) {
+ case kAriesSpotID:
+ movieName = "Images/AI/Globals/OMAI";
+ break;
+ case kMercurySpotID:
+ movieName = "Images/AI/Globals/OMMI";
+ break;
+ case kPoseidonSpotID:
+ movieName = "Images/AI/Globals/OMPI";
+ break;
+ }
+
+ ItemState state = getItemState(), newState;
+ switch (state) {
+ case kOptical001:
+ newState = kOptical002;
+ break;
+ case kOptical010:
+ newState = kOptical020;
+ break;
+ case kOptical011:
+ if (id == kAriesSpotID)
+ newState = kOptical012;
+ else
+ newState = kOptical021;
+ break;
+ case kOptical100:
+ newState = kOptical200;
+ break;
+ case kOptical101:
+ if (id == kAriesSpotID)
+ newState = kOptical102;
+ else
+ newState = kOptical201;
+ break;
+ case kOptical110:
+ if (id == kMercurySpotID)
+ newState = kOptical120;
+ else
+ newState = kOptical210;
+ break;
+ case kOptical111:
+ if (id == kAriesSpotID)
+ newState = kOptical112;
+ else if (id == kMercurySpotID)
+ newState = kOptical121;
+ else
+ newState = kOptical211;
+ break;
+ case kOptical000: // Can never happen.
+ default:
+ error("Invalid optical chip state");
+ }
+
+ setItemState(newState);
+
+ if (g_AIArea)
+ g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kOpticalInterruption);
+
+ setItemState(state);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/opticalchip.h b/engines/pegasus/items/biochips/opticalchip.h
new file mode 100644
index 0000000000..2f66f73d3a
--- /dev/null
+++ b/engines/pegasus/items/biochips/opticalchip.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H
+#define PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H
+
+#include "pegasus/hotspot.h"
+#include "pegasus/util.h"
+#include "pegasus/items/biochips/biochipitem.h"
+
+namespace Pegasus {
+
+class OpticalChip : public BiochipItem {
+public:
+ OpticalChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~OpticalChip();
+
+ virtual void writeToStream(Common::WriteStream *);
+ virtual void readFromStream(Common::ReadStream *);
+
+ void addAries();
+ void addMercury();
+ void addPoseidon();
+
+ void activateOpticalHotspots();
+ void clickInOpticalHotspot(HotSpotID);
+ void playOpMemMovie(HotSpotID);
+
+protected:
+ enum {
+ kOpticalAriesExposed,
+ kOpticalMercuryExposed,
+ kOpticalPoseidonExposed,
+ kNumOpticalChipFlags
+ };
+
+ void setUpOpticalChip();
+
+ FlagsArray<byte, kNumOpticalChipFlags> _opticalFlags;
+ Hotspot _ariesHotspot;
+ Hotspot _mercuryHotspot;
+ Hotspot _poseidonHotspot;
+};
+
+extern OpticalChip *g_opticalChip;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/biochips/pegasuschip.cpp b/engines/pegasus/items/biochips/pegasuschip.cpp
new file mode 100644
index 0000000000..fa551fce30
--- /dev/null
+++ b/engines/pegasus/items/biochips/pegasuschip.cpp
@@ -0,0 +1,198 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/pegasuschip.h"
+#include "pegasus/neighborhood/tsa/fulltsa.h"
+#include "pegasus/neighborhood/tsa/tinytsa.h"
+
+namespace Pegasus {
+
+PegasusChip::PegasusChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ BiochipItem(id, neighborhood, room, direction), _recallSpot(kPegasusRecallSpotID) {
+ _recallSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 116, kAIMiddleAreaTop + 63, kAIMiddleAreaLeft + 184, kAIMiddleAreaTop + 91));
+ _recallSpot.setHotspotFlags(kPegasusBiochipSpotFlag);
+ g_allHotspots.push_back(&_recallSpot);
+ setItemState(kPegasusTSA00);
+}
+
+PegasusChip::~PegasusChip() {
+ g_allHotspots.removeOneHotspot(kPegasusRecallSpotID);
+}
+
+void PegasusChip::select() {
+ BiochipItem::select();
+ setUpPegasusChip();
+}
+
+void PegasusChip::setUpPegasusChip() {
+ switch (GameState.getCurrentNeighborhood()) {
+ case kCaldoriaID:
+ setItemState(kPegasusCaldoria);
+ break;
+ case kFullTSAID:
+ case kFinalTSAID:
+ case kTinyTSAID:
+ setItemState(kPegasusTSA10);
+ break;
+ case kPrehistoricID:
+ if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog))
+ setItemState(kPegasusPrehistoric00);
+ else
+ setItemState(kPegasusPrehistoric10);
+ break;
+ case kMarsID:
+ if (GameState.getMarsFinished())
+ setItemState(kPegasusMars00);
+ else
+ setItemState(kPegasusMars10);
+ break;
+ case kWSCID:
+ if (GameState.getWSCFinished())
+ setItemState(kPegasusWSC00);
+ else
+ setItemState(kPegasusWSC10);
+ break;
+ case kNoradAlphaID:
+ case kNoradDeltaID:
+ if (GameState.getNoradFinished())
+ setItemState(kPegasusNorad00);
+ else
+ setItemState(kPegasusNorad10);
+ break;
+ }
+}
+
+// Only does something if the chip should be announcing that the time zone is finished...
+void PegasusChip::setUpPegasusChipRude() {
+ switch (GameState.getCurrentNeighborhood()) {
+ case kPrehistoricID:
+ if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog))
+ setItemState(kPegasusPrehistoric00);
+ break;
+ case kMarsID:
+ if (GameState.getMarsFinished())
+ setItemState(kPegasusMars00);
+ break;
+ case kWSCID:
+ if (GameState.getWSCFinished())
+ setItemState(kPegasusWSC00);
+ break;
+ case kNoradAlphaID:
+ case kNoradDeltaID:
+ if (GameState.getNoradFinished())
+ setItemState(kPegasusNorad00);
+ break;
+ }
+}
+
+void PegasusChip::activatePegasusHotspots() {
+ switch (GameState.getCurrentNeighborhood()) {
+ case kPrehistoricID:
+ // WORKAROUND: Don't allow the player to recall if they don't have
+ // the historical log. Otherwise, gameplay is broken when returning
+ // to the TSA.
+ if (!((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog))
+ return;
+ // fall through
+ case kMarsID:
+ case kWSCID:
+ case kNoradAlphaID:
+ case kNoradDeltaID:
+ _recallSpot.setActive();
+ break;
+ }
+}
+
+void PegasusChip::clickInPegasusHotspot() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ ItemState thisState = getItemState();
+ ItemState hiliteState;
+
+ switch (thisState) {
+ case kPegasusPrehistoric00:
+ hiliteState = kPegasusPrehistoric01;
+ break;
+ case kPegasusPrehistoric10:
+ hiliteState = kPegasusPrehistoric11;
+ break;
+ case kPegasusMars00:
+ hiliteState = kPegasusMars01;
+ break;
+ case kPegasusMars10:
+ hiliteState = kPegasusMars11;
+ break;
+ case kPegasusNorad00:
+ hiliteState = kPegasusNorad01;
+ break;
+ case kPegasusNorad10:
+ hiliteState = kPegasusNorad11;
+ break;
+ case kPegasusWSC00:
+ hiliteState = kPegasusWSC01;
+ break;
+ case kPegasusWSC10:
+ hiliteState = kPegasusWSC11;
+ break;
+ default:
+ error("Invalid pegasus chip state");
+ }
+
+ // WORKAROUND: The original called setItemState() here. However,
+ // since we're overriding select() to call setUpPegasusChip(),
+ // the highlighted frame is never displayed! So, we're manually
+ // setting the state and selecting the item. Also of note is that
+ // setItemState() for this class is effectively useless since it
+ // always gets overriden in the select() function. The only reason
+ // that this doesn't end in infinite recursion is because setItemState()
+ // has a check against the current state to make sure you don't call
+ // select() again. </rant>
+ _itemState = hiliteState;
+ BiochipItem::select();
+
+ uint32 time = g_system->getMillis();
+ while (g_system->getMillis() < time + 500) {
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ setItemState(thisState);
+
+ if (!((Neighborhood *)g_neighborhood)->okayToJump())
+ return;
+
+ if (g_energyMonitor)
+ g_energyMonitor->stopEnergyDraining();
+
+ if (GameState.getTSAState() == kPlayerWentToPrehistoric || GameState.allTimeZonesFinished())
+ vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth);
+ else
+ vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/pegasuschip.h b/engines/pegasus/items/biochips/pegasuschip.h
new file mode 100644
index 0000000000..7597424821
--- /dev/null
+++ b/engines/pegasus/items/biochips/pegasuschip.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H
+#define PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H
+
+#include "pegasus/hotspot.h"
+#include "pegasus/items/biochips/biochipitem.h"
+
+namespace Pegasus {
+
+class PegasusChip : public BiochipItem {
+public:
+ PegasusChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~PegasusChip();
+
+ void select();
+
+ void setUpPegasusChip();
+
+ // Called to set up the Pegasus chip when the Pegasus chip is the current chip but does not
+ // own the center area.
+ void setUpPegasusChipRude();
+ void activatePegasusHotspots();
+ void clickInPegasusHotspot();
+
+protected:
+ Hotspot _recallSpot;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/biochips/retscanchip.cpp b/engines/pegasus/items/biochips/retscanchip.cpp
new file mode 100644
index 0000000000..84b74a63d2
--- /dev/null
+++ b/engines/pegasus/items/biochips/retscanchip.cpp
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/retscanchip.h"
+
+namespace Pegasus {
+
+RetScanChip::RetScanChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ BiochipItem(id, neighborhood, room, direction) {
+}
+
+void RetScanChip::searchForLaser() {
+ ItemExtraEntry entry;
+ findItemExtra(kRetinalScanSearching, entry);
+
+ if (g_AIArea)
+ g_AIArea->playAIAreaSequence(kBiochipSignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop);
+
+ findItemExtra(kRetinalScanActivated, entry);
+ if (g_AIArea)
+ g_AIArea->playAIAreaSequence(kBiochipSignature, kRightAreaSignature, entry.extraStart, entry.extraStop);
+
+ setItemState(kRetinalSimulating);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/retscanchip.h b/engines/pegasus/items/biochips/retscanchip.h
new file mode 100644
index 0000000000..153e6cd071
--- /dev/null
+++ b/engines/pegasus/items/biochips/retscanchip.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H
+#define PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H
+
+#include "pegasus/items/biochips/biochipitem.h"
+
+namespace Pegasus {
+
+class RetScanChip : public BiochipItem {
+public:
+ RetScanChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~RetScanChip() {}
+
+ void searchForLaser();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/biochips/shieldchip.cpp b/engines/pegasus/items/biochips/shieldchip.cpp
new file mode 100644
index 0000000000..58cbfcc4ec
--- /dev/null
+++ b/engines/pegasus/items/biochips/shieldchip.cpp
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/items/biochips/shieldchip.h"
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+ShieldChip *g_shield = 0;
+
+ShieldChip::ShieldChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ BiochipItem(id, neighborhood, room, direction) {
+ g_shield = this;
+}
+
+void ShieldChip::select() {
+ BiochipItem::select();
+ GameState.setShieldOn(true);
+ if (g_neighborhood)
+ g_neighborhood->shieldOn();
+}
+
+void ShieldChip::deselect() {
+ BiochipItem::deselect();
+ GameState.setShieldOn(false);
+ if (g_neighborhood)
+ g_neighborhood->shieldOff();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/shieldchip.h b/engines/pegasus/items/biochips/shieldchip.h
new file mode 100644
index 0000000000..69c6369236
--- /dev/null
+++ b/engines/pegasus/items/biochips/shieldchip.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H
+#define PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H
+
+#include "pegasus/items/biochips/biochipitem.h"
+
+namespace Pegasus {
+
+class ShieldChip : public BiochipItem {
+public:
+ ShieldChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~ShieldChip() {}
+
+ void select();
+ void deselect();
+};
+
+extern ShieldChip *g_shield;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/inventory.cpp b/engines/pegasus/items/inventory.cpp
new file mode 100644
index 0000000000..57923b105d
--- /dev/null
+++ b/engines/pegasus/items/inventory.cpp
@@ -0,0 +1,175 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/constants.h"
+#include "pegasus/items/item.h"
+#include "pegasus/items/inventory.h"
+
+namespace Pegasus {
+
+Inventory::Inventory() {
+ _weightLimit = 100;
+ _ownerID = kNoActorID;
+ _referenceCount = 0;
+}
+
+Inventory::~Inventory() {
+}
+
+void Inventory::setWeightLimit(WeightType limit) {
+ _weightLimit = limit;
+ // *** What to do if the new weight limit is greater than the current weight?
+}
+
+WeightType Inventory::getWeight() {
+ WeightType result = 0;
+
+ for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++)
+ result += (*it)->getItemWeight();
+
+ return result;
+}
+
+// If the item already belongs, just return kInventoryOK.
+InventoryResult Inventory::addItem(Item *item) {
+ if (itemInInventory(item))
+ return kInventoryOK;
+
+ if (getWeight() + item->getItemWeight() > _weightLimit)
+ return kTooMuchWeight;
+
+ _inventoryList.push_back(item);
+ item->setItemOwner(_ownerID);
+
+ ++_referenceCount;
+ return kInventoryOK;
+}
+
+InventoryResult Inventory::removeItem(Item *item) {
+ for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) {
+ if (*it == item) {
+ _inventoryList.erase(it);
+ item->setItemOwner(kNoActorID);
+
+ ++_referenceCount;
+ return kInventoryOK;
+ }
+ }
+
+ return kItemNotInInventory;
+}
+
+InventoryResult Inventory::removeItem(ItemID id) {
+ Item *item = findItemByID(id);
+
+ if (item) {
+ _inventoryList.remove(item);
+ item->setItemOwner(kNoActorID);
+
+ ++_referenceCount;
+ return kInventoryOK;
+ }
+
+ return kItemNotInInventory;
+}
+
+void Inventory::removeAllItems() {
+ _inventoryList.clear();
+ ++_referenceCount;
+}
+
+bool Inventory::itemInInventory(Item *item) {
+ for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++)
+ if (*it == item)
+ return true;
+
+ return false;
+}
+
+bool Inventory::itemInInventory(ItemID id) {
+ return findItemByID(id) != NULL;
+}
+
+Item *Inventory::getItemAt(int32 index) {
+ int32 i = 0;
+ for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++)
+ if (i == index)
+ return *it;
+
+ return 0;
+}
+
+ItemID Inventory::getItemIDAt(int32 index) {
+ Item *item = getItemAt(index);
+
+ if (item)
+ return item->getObjectID();
+
+ return kNoItemID;
+}
+
+Item *Inventory::findItemByID(ItemID id) {
+ return _inventoryList.findItemByID(id);
+}
+
+// Return -1 if not found.
+
+int32 Inventory::findIndexOf(Item *item) {
+ uint32 i = 0;
+ for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++)
+ if (*it == item)
+ return i;
+
+ return -1;
+}
+
+// Return -1 if not found.
+
+int32 Inventory::findIndexOf(ItemID id) {
+ uint32 i = 0;
+ for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++)
+ if ((*it)->getObjectID() == id)
+ return i;
+
+ return -1;
+}
+
+WeightType Inventory::getWeightLimit() {
+ return _weightLimit;
+}
+
+int32 Inventory::getNumItems() {
+ return _inventoryList.size();
+}
+
+void Inventory::setOwnerID(const ActorID id) {
+ _ownerID = id;
+}
+
+ActorID Inventory::getOwnerID() const {
+ return _ownerID;
+}
+
+} // End of namespae Pegasus
diff --git a/engines/pegasus/items/inventory.h b/engines/pegasus/items/inventory.h
new file mode 100644
index 0000000000..796ec49556
--- /dev/null
+++ b/engines/pegasus/items/inventory.h
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_INVENTORY_H
+#define PEGASUS_ITEMS_INVENTORY_H
+
+#include "pegasus/types.h"
+#include "pegasus/items/itemlist.h"
+
+namespace Pegasus {
+
+class Item;
+
+// Inventories have a "current item". This item is the default item the player can
+// use. In a text adventure system, the current item would be "it", as in
+// "Hit the troll with it," where "it" would refer to some weapon which is the current
+// item. In a graphic adventure, the current item would be the item the user selects
+// to use with the mouse or other pointing device.
+
+class Inventory {
+public:
+ Inventory();
+ virtual ~Inventory();
+
+ WeightType getWeightLimit();
+ void setWeightLimit(WeightType limit);
+ WeightType getWeight();
+
+ virtual InventoryResult addItem(Item *item);
+ virtual InventoryResult removeItem(Item *item);
+ virtual InventoryResult removeItem(ItemID id);
+ virtual bool itemInInventory(Item *item);
+ virtual bool itemInInventory(ItemID id);
+ virtual Item *getItemAt(int32 index);
+ virtual ItemID getItemIDAt(int32 index);
+ virtual Item *findItemByID(ItemID id);
+ virtual int32 findIndexOf(Item *item);
+ virtual int32 findIndexOf(ItemID id);
+ int32 getNumItems();
+ virtual void removeAllItems();
+
+ void setOwnerID(const ActorID id);
+ ActorID getOwnerID() const;
+
+ uint32 getReferenceCount() { return _referenceCount; }
+
+protected:
+ WeightType _weightLimit;
+ ActorID _ownerID;
+ ItemList _inventoryList;
+
+private:
+ uint32 _referenceCount;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/inventory/airmask.cpp b/engines/pegasus/items/inventory/airmask.cpp
new file mode 100644
index 0000000000..c65dd36102
--- /dev/null
+++ b/engines/pegasus/items/inventory/airmask.cpp
@@ -0,0 +1,249 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+AirMask *g_airMask = 0;
+
+// Based on full == 100, which is scale used by GetAirLeft().
+static const TimeValue kOxygenLowThreshold = 25;
+
+void AirMask::airMaskTimerExpired() {
+ if (g_neighborhood)
+ g_neighborhood->checkAirMask();
+}
+
+AirMask::AirMask(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ InventoryItem(id, neighborhood, room, direction), _toggleSpot(kAirMaskToggleSpotID) {
+ g_airMask = this;
+ _toggleSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 17, kAIMiddleAreaLeft + 110, kAIMiddleAreaTop + 57));
+ _toggleSpot.setHotspotFlags(kAirMaskSpotFlag);
+ g_allHotspots.push_back(&_toggleSpot);
+ setItemState(kAirMaskEmptyOff);
+ _oxygenTimer.primeFuse(0);
+ _oxygenTimer.setFunctor(new Common::Functor0Mem<void, AirMask>(this, &AirMask::airMaskTimerExpired));
+}
+
+AirMask::~AirMask() {
+ g_allHotspots.removeOneHotspot(kAirMaskToggleSpotID);
+ g_airMask = 0;
+}
+
+void AirMask::writeToStream(Common::WriteStream *stream) {
+ InventoryItem::writeToStream(stream);
+ stream->writeUint32BE(_oxygenTimer.getTimeRemaining());
+}
+
+void AirMask::readFromStream(Common::ReadStream *stream) {
+ _oxygenTimer.stopFuse();
+ InventoryItem::readFromStream(stream);
+ _oxygenTimer.primeFuse(stream->readUint32BE());
+}
+
+void AirMask::putMaskOn() {
+ AirQuality airQuality;
+
+ if (g_neighborhood)
+ airQuality = g_neighborhood->getAirQuality(GameState.getCurrentRoom());
+ else
+ airQuality = kAirQualityGood;
+
+ uint airLevel = getAirLeft();
+ ItemState newState = getItemState();
+ ItemState oldState = newState;
+
+ if (airLevel == 0) {
+ newState = kAirMaskEmptyFilter;
+ } else if (airLevel <= kOxygenLowThreshold) {
+ if (airQuality == kAirQualityVacuum)
+ newState = kAirMaskLowOn;
+ else
+ newState = kAirMaskLowFilter;
+ } else {
+ if (airQuality == kAirQualityVacuum)
+ newState = kAirMaskFullOn;
+ else
+ newState = kAirMaskFullFilter;
+ }
+
+ if (newState != oldState)
+ setItemState(newState);
+}
+
+void AirMask::takeMaskOff() {
+ uint airLevel = getAirLeft();
+ ItemState newState = getItemState();
+ ItemState oldState = newState;
+
+ if (airLevel == 0)
+ newState = kAirMaskEmptyOff;
+ else if (airLevel <= kOxygenLowThreshold)
+ newState = kAirMaskLowOff;
+ else
+ newState = kAirMaskFullOff;
+
+ if (newState != oldState)
+ setItemState(newState);
+}
+
+void AirMask::toggleItemState() {
+ if (isAirMaskInUse())
+ takeMaskOff();
+ else
+ putMaskOn();
+}
+
+void AirMask::airQualityChanged() {
+ if (isAirMaskInUse())
+ putMaskOn();
+ else
+ takeMaskOff();
+}
+
+void AirMask::setItemState(const ItemState newState) {
+ if (newState != getItemState()) {
+ InventoryItem::setItemState(newState);
+
+ switch (newState) {
+ case kAirMaskFullOn:
+ case kAirMaskLowOn:
+ if (!_oxygenTimer.isFuseLit()) {
+ _oxygenTimer.lightFuse();
+ startIdling();
+ }
+ break;
+ default:
+ if (_oxygenTimer.isFuseLit()) {
+ _oxygenTimer.stopFuse();
+ stopIdling();
+ }
+ break;
+ }
+
+ if (g_neighborhood)
+ g_neighborhood->checkAirMask();
+
+ g_AIArea->checkMiddleArea();
+ }
+}
+
+void AirMask::useIdleTime() {
+ if (getAirLeft() == 0)
+ setItemState(kAirMaskEmptyOff);
+ else if (getAirLeft() <= kOxygenLowThreshold)
+ setItemState(kAirMaskLowOn);
+}
+
+void AirMask::refillAirMask() {
+ switch (getItemState()) {
+ case kAirMaskEmptyOff:
+ case kAirMaskLowOff:
+ setItemState(kAirMaskFullOff);
+ break;
+ case kAirMaskEmptyFilter:
+ case kAirMaskLowFilter:
+ setItemState(kAirMaskFullFilter);
+ break;
+ case kAirMaskLowOn:
+ setItemState(kAirMaskFullOn);
+ break;
+ }
+
+ if (_oxygenTimer.isFuseLit()) {
+ _oxygenTimer.stopFuse();
+ _oxygenTimer.primeFuse(kOxyMaskFullTime);
+ _oxygenTimer.lightFuse();
+ } else {
+ _oxygenTimer.primeFuse(kOxyMaskFullTime);
+ }
+}
+
+// Doesn't return 0 until the timer is actually at 0.
+uint AirMask::getAirLeft() {
+ return CLIP<int>(((_oxygenTimer.getTimeRemaining() * 100) + kOxyMaskFullTime - 1) / kOxyMaskFullTime, 0, 100);
+}
+
+bool AirMask::isAirMaskInUse() {
+ switch (getItemState()) {
+ case kAirMaskEmptyOff:
+ case kAirMaskLowOff:
+ case kAirMaskFullOff:
+ return false;
+ break;
+ default:
+ return true;
+ break;
+ }
+}
+
+bool AirMask::isAirMaskOn() {
+ switch (getItemState()) {
+ case kAirMaskLowOn:
+ case kAirMaskFullOn:
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+}
+
+bool AirMask::isAirFilterOn() {
+ switch (getItemState()) {
+ case kAirMaskEmptyFilter:
+ case kAirMaskLowFilter:
+ case kAirMaskFullFilter:
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+}
+
+void AirMask::addedToInventory() {
+ GameState.setMarsMaskOnFiller(false);
+}
+
+void AirMask::removedFromInventory() {
+ if (isAirMaskInUse())
+ toggleItemState();
+}
+
+void AirMask::activateAirMaskHotspots() {
+ _toggleSpot.setActive();
+}
+
+void AirMask::clickInAirMaskHotspot() {
+ toggleItemState();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/inventory/airmask.h b/engines/pegasus/items/inventory/airmask.h
new file mode 100644
index 0000000000..6a2d708a6c
--- /dev/null
+++ b/engines/pegasus/items/inventory/airmask.h
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_INVENTORY_AIRMASK_H
+#define PEGASUS_ITEMS_INVENTORY_AIRMASK_H
+
+#include "pegasus/hotspot.h"
+#include "pegasus/timers.h"
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+class AirMask : public InventoryItem, private Idler {
+public:
+ AirMask(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~AirMask();
+
+ virtual void writeToStream(Common::WriteStream *);
+ virtual void readFromStream(Common::ReadStream *);
+
+ virtual void setItemState(const ItemState);
+ void putMaskOn();
+ void takeMaskOff();
+ void toggleItemState();
+ void airQualityChanged();
+
+ bool isAirMaskInUse();
+ bool isAirMaskOn();
+ bool isAirFilterOn();
+
+ void refillAirMask();
+
+ // Returns a percentage
+ uint getAirLeft();
+
+ void activateAirMaskHotspots();
+ void clickInAirMaskHotspot();
+
+protected:
+ void airMaskTimerExpired();
+
+ virtual void removedFromInventory();
+ virtual void addedToInventory();
+ void useIdleTime();
+
+ Hotspot _toggleSpot;
+ FuseFunction _oxygenTimer;
+};
+
+extern AirMask *g_airMask;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/inventory/gascanister.cpp b/engines/pegasus/items/inventory/gascanister.cpp
new file mode 100644
index 0000000000..bf63cc6542
--- /dev/null
+++ b/engines/pegasus/items/inventory/gascanister.cpp
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/inventory/gascanister.h"
+
+namespace Pegasus {
+
+GasCanister::GasCanister(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ InventoryItem(id, neighborhood, room, direction) {
+}
+
+void GasCanister::select() {
+ InventoryItem::select();
+ takeSharedArea();
+}
+
+void GasCanister::takeSharedArea() {
+ ItemExtraEntry entry;
+ findItemExtra(kGasCanLoop, entry);
+ g_AIArea->loopAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/inventory/gascanister.h b/engines/pegasus/items/inventory/gascanister.h
new file mode 100644
index 0000000000..7d4d8193f5
--- /dev/null
+++ b/engines/pegasus/items/inventory/gascanister.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_INVENTORY_GASCANISTER_H
+#define PEGASUS_ITEMS_INVENTORY_GASCANISTER_H
+
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+class GasCanister : public InventoryItem {
+public:
+ GasCanister(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~GasCanister() {}
+
+ void select();
+ void takeSharedArea();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/inventory/inventoryitem.cpp b/engines/pegasus/items/inventory/inventoryitem.cpp
new file mode 100644
index 0000000000..4399708879
--- /dev/null
+++ b/engines/pegasus/items/inventory/inventoryitem.cpp
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/stream.h"
+
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+InventoryItem::InventoryItem(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ Item(id, neighborhood, room, direction) {
+
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ Common::SeekableReadStream *leftInfo = vm->_resFork->getResource(MKTAG('L', 'e', 'f', 't'), kItemBaseResID + id);
+ if (leftInfo) {
+ _leftAreaInfo = readItemState(leftInfo);
+ delete leftInfo;
+ } else {
+ _leftAreaInfo.numEntries = 0;
+ _leftAreaInfo.entries = 0;
+ }
+
+ Common::SeekableReadStream *inventoryInfo = vm->_resFork->getResource(MKTAG('I', 'n', 'v', 'I'), kItemBaseResID + id);
+ if (inventoryInfo) {
+ _inventoryInfo.panelStart = inventoryInfo->readUint32BE();
+ _inventoryInfo.panelStop = inventoryInfo->readUint32BE();
+ delete inventoryInfo;
+ } else {
+ _inventoryInfo.panelStart = _inventoryInfo.panelStop = 0;
+ }
+
+ _itemAnimationTime = 0;
+}
+
+InventoryItem::~InventoryItem() {
+ delete[] _leftAreaInfo.entries;
+}
+
+ItemType InventoryItem::getItemType() {
+ return kInventoryItemType;
+}
+
+TimeValue InventoryItem::getLeftAreaTime() const {
+ if (!_leftAreaInfo.entries)
+ return 0xffffffff;
+
+ TimeValue time;
+ ItemState state;
+
+ findItemStateEntryByState(_leftAreaInfo, _itemState, time);
+ if (time == 0xffffffff)
+ getItemStateEntry(_leftAreaInfo, 0, state, time);
+
+ return time;
+}
+
+void InventoryItem::setAnimationTime(const TimeValue time) {
+ _itemAnimationTime = time;
+}
+
+TimeValue InventoryItem::getAnimationTime() const {
+ return _itemAnimationTime;
+}
+
+// Must affect images in left area.
+void InventoryItem::select() {
+ Item::select();
+
+ if (g_AIArea)
+ g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, getLeftAreaTime());
+}
+
+void InventoryItem::deselect() {
+ Item::deselect();
+
+ if (g_AIArea)
+ g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, 0xffffffff);
+}
+
+void InventoryItem::getPanelTimes(TimeValue &start, TimeValue &stop) const {
+ start = _inventoryInfo.panelStart;
+ stop = _inventoryInfo.panelStop;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/inventory/inventoryitem.h b/engines/pegasus/items/inventory/inventoryitem.h
new file mode 100644
index 0000000000..9d78113014
--- /dev/null
+++ b/engines/pegasus/items/inventory/inventoryitem.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H
+#define PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H
+
+#include "pegasus/items/item.h"
+
+namespace Pegasus {
+
+// JMPInventoryInfo contains the resource data used by InventoryItems.
+
+struct JMPInventoryInfo {
+ TimeValue panelStart;
+ TimeValue panelStop;
+};
+
+class InventoryItem : public Item {
+public:
+ InventoryItem(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~InventoryItem();
+
+ virtual ItemType getItemType();
+
+ void getPanelTimes(TimeValue &, TimeValue &) const;
+ TimeValue getLeftAreaTime() const;
+
+ void setAnimationTime(const TimeValue);
+ TimeValue getAnimationTime() const;
+
+ virtual void toggleItemState() {}
+
+ // Must affect images in left area.
+ virtual void select();
+ virtual void deselect();
+
+protected:
+ JMPInventoryInfo _inventoryInfo;
+ ItemStateInfo _leftAreaInfo;
+ TimeValue _itemAnimationTime;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/inventory/keycard.cpp b/engines/pegasus/items/inventory/keycard.cpp
new file mode 100644
index 0000000000..c818b6675b
--- /dev/null
+++ b/engines/pegasus/items/inventory/keycard.cpp
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/items/inventory/keycard.h"
+
+namespace Pegasus {
+
+KeyCard::KeyCard(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+ InventoryItem(id, neighborhood, room, direction) {
+ setItemState(kFlashlightOff);
+}
+
+void KeyCard::toggleItemState() {
+ if (getItemState() == kFlashlightOff)
+ setItemState(kFlashlightOn);
+ else
+ setItemState(kFlashlightOff);
+}
+
+void KeyCard::setItemState(const ItemState newState) {
+ if (newState != getItemState()) {
+ InventoryItem::setItemState(newState);
+ ((PegasusEngine *)g_engine)->checkFlashlight();
+ }
+}
+
+bool KeyCard::isFlashlightOn() {
+ return getItemState() == kFlashlightOn;
+}
+
+void KeyCard::removedFromInventory() {
+ if (isFlashlightOn())
+ setItemState(kFlashlightOff);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/inventory/keycard.h b/engines/pegasus/items/inventory/keycard.h
new file mode 100644
index 0000000000..846f40e6e5
--- /dev/null
+++ b/engines/pegasus/items/inventory/keycard.h
@@ -0,0 +1,48 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_INVENTORY_KEYCARD_H
+#define PEGASUS_ITEMS_INVENTORY_KEYCARD_H
+
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+class KeyCard : public InventoryItem {
+public:
+ KeyCard(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+ virtual ~KeyCard() {}
+
+ virtual void toggleItemState();
+ virtual void setItemState(const ItemState);
+ bool isFlashlightOn();
+
+protected:
+ virtual void removedFromInventory();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/inventorypicture.cpp b/engines/pegasus/items/inventorypicture.cpp
new file mode 100644
index 0000000000..fc812faae2
--- /dev/null
+++ b/engines/pegasus/items/inventorypicture.cpp
@@ -0,0 +1,370 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/transition.h"
+#include "pegasus/items/inventorypicture.h"
+#include "pegasus/items/biochips/biochipitem.h"
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+InventoryPicture::InventoryPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) :
+ InputHandler(nextHandler), Picture(id), _panelMovie(kNoDisplayElement){
+ _inventory = inventory;
+ _lastReferenceCount = 0xffffffff;
+
+ if (_inventory->getNumItems() > 0) {
+ _currentItemIndex = 0;
+ _currentItem = (Item *)_inventory->getItemAt(0);
+ } else {
+ _currentItemIndex = -1;
+ _currentItem = 0;
+ }
+
+ _active = false;
+ _shouldDrawHighlight = true;
+ _itemsPerRow = 1;
+ _numberOfRows = 1;
+ _itemWidth = 0;
+ _itemHeight = 0;
+ _itemX = 0;
+ _itemY = 0;
+}
+
+void InventoryPicture::initInventoryImage(Transition *transition) {
+ initFromPICTFile(_pictName, true);
+ _panelMovie.shareSurface(this);
+ _panelMovie.initFromMovieFile(_movieName);
+ _panelMovie.getBounds(_highlightBounds);
+ _panelMovie.setTriggeredElement(transition);
+ _highlightImage.initFromPICTFile(_highlightName, true);
+}
+
+void InventoryPicture::throwAwayInventoryImage() {
+ if (isSurfaceValid()) {
+ _panelMovie.releaseMovie();
+ _highlightImage.deallocateSurface();
+ deallocateSurface();
+ }
+}
+
+void InventoryPicture::getItemXY(uint32 index, CoordType &x, CoordType &y) {
+ x = (index % _itemsPerRow) * _itemWidth + _itemX;
+ y = (index / _itemsPerRow) * _itemHeight + _itemY;
+}
+
+void InventoryPicture::drawItemHighlight(const Common::Rect &r) {
+ if (_highlightImage.isSurfaceValid()) {
+ Common::Rect r2 = _highlightBounds;
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ r2.translate(bounds.left, bounds.top);
+ r2 = r2.findIntersectingRect(r);
+ if (!r2.isEmpty()) {
+ Common::Rect r1 = r2;
+ r1.translate(-bounds.left - _highlightBounds.left, -bounds.top - _highlightBounds.top);
+ _highlightImage.drawImage(r1, r2);
+ }
+ }
+}
+
+void InventoryPicture::draw(const Common::Rect &r) {
+ Picture::draw(r);
+ if (_inventory->getNumItems() != 0 && _shouldDrawHighlight)
+ drawItemHighlight(r);
+}
+
+// Assumes index >= 0.
+void InventoryPicture::setCurrentItemIndex(int32 index) {
+ if (index >= _inventory->getNumItems())
+ index = _inventory->getNumItems() - 1;
+
+ Item *currentItem = 0;
+ if (index >= 0)
+ currentItem = (Item *)_inventory->getItemAt(index);
+
+ if (currentItem != _currentItem) {
+ if (_currentItem) {
+ if (_currentItem->isSelected())
+ _currentItem->deselect();
+
+ if (_active)
+ unhighlightCurrentItem();
+ }
+
+ _currentItemIndex = index;
+ _currentItem = currentItem;
+ if (_currentItem) {
+ _currentItem->select();
+
+ if (_active)
+ highlightCurrentItem();
+ }
+
+ if (_active)
+ triggerRedraw();
+ }
+}
+
+void InventoryPicture::setCurrentItemID(ItemID id) {
+ int32 index = _inventory->findIndexOf(id);
+ if (index >= 0)
+ setCurrentItemIndex(index);
+}
+
+InventoryResult InventoryPicture::addInventoryItem(Item *item) {
+ InventoryResult result = _inventory->addItem(item);
+
+ if (result == kInventoryOK)
+ setCurrentItemIndex(_inventory->findIndexOf(item));
+
+ return result;
+}
+
+InventoryResult InventoryPicture::removeInventoryItem(Item *item) {
+ InventoryResult result = _inventory->removeItem(item);
+
+ if (result == kInventoryOK)
+ setCurrentItemIndex(getCurrentItemIndex());
+
+ return result;
+}
+
+void InventoryPicture::removeAllItems() {
+ _inventory->removeAllItems();
+ setCurrentItemIndex(0);
+}
+
+bool InventoryPicture::itemInInventory(Item *item) {
+ return _inventory->itemInInventory(item);
+}
+
+bool InventoryPicture::itemInInventory(const ItemID id) {
+ return _inventory->itemInInventory(id);
+}
+
+void InventoryPicture::panelUp() {
+ allowInput(true);
+}
+
+// Must ensure that the picture matches the _inventory member variable.
+void InventoryPicture::activateInventoryPicture() {
+ if (_active)
+ return;
+
+ allowInput(false);
+
+ if (_lastReferenceCount != _inventory->getReferenceCount()) {
+ uint32 numItems = _inventory->getNumItems();
+
+ CoordType x, y;
+ getItemXY(0, x, y);
+ _panelMovie.moveMovieBoxTo(x, y);
+ _panelMovie.show();
+
+ for (uint32 i = 0; i < numItems; i++) {
+ Item *item = (Item *)_inventory->getItemAt(i);
+ if (item == _currentItem)
+ item->select();
+
+ getItemXY(i, x, y);
+ _panelMovie.moveMovieBoxTo(x, y);
+ _panelMovie.setTime(getItemPanelTime(item));
+ _panelMovie.redrawMovieWorld();
+ }
+
+ uint32 numSlots = _itemsPerRow * _numberOfRows;
+
+ for (uint32 i = numItems; i < numSlots; i++) {
+ getItemXY(i, x, y);
+ _panelMovie.moveMovieBoxTo(x, y);
+ _panelMovie.setTime(0);
+ _panelMovie.redrawMovieWorld();
+ }
+
+ _lastReferenceCount = _inventory->getReferenceCount();
+ }
+
+ show(); // *** Do we really need this?
+ if (_currentItem)
+ highlightCurrentItem();
+
+ _active = true;
+}
+
+void InventoryPicture::deactivateInventoryPicture() {
+ if (!_active)
+ return;
+
+ _active = false;
+ allowInput(false);
+ _panelMovie.hide();
+ hide();
+
+ if (_inventory->getNumItems() != 0)
+ if (!_currentItem->isActive())
+ _currentItem->activate();
+}
+
+void InventoryPicture::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (_active) {
+ if (input.upButtonDown()) {
+ if (_currentItemIndex - _itemsPerRow >= 0)
+ setCurrentItemIndex(_currentItemIndex - _itemsPerRow);
+ } else if (input.downButtonDown()) {
+ if (_currentItemIndex + _itemsPerRow < _inventory->getNumItems())
+ setCurrentItemIndex(_currentItemIndex + _itemsPerRow);
+ } else if (input.leftButtonDown()) {
+ if ((_currentItemIndex % _itemsPerRow) != 0)
+ setCurrentItemIndex(_currentItemIndex - 1);
+ } else if (input.rightButtonDown()) {
+ if (((_currentItemIndex + 1) % _itemsPerRow) != 0 && _currentItemIndex + 1 < _inventory->getNumItems())
+ setCurrentItemIndex(_currentItemIndex + 1);
+ }
+ }
+
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+void InventoryPicture::highlightCurrentItem() {
+ CoordType x, y;
+ getItemXY(_currentItemIndex, x, y);
+ _highlightBounds.moveTo(x, y);
+}
+
+InventoryItemsPicture::InventoryItemsPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) :
+ InventoryPicture(id, nextHandler, inventory) {
+ _pictName = "Images/Items/Inventory/Inventory Panel";
+ _movieName = "Images/Items/Inventory/Inventory Panel Movie";
+ _highlightName = "Images/Items/Inventory/Inventory Hilite";
+
+ _itemsPerRow = 3;
+ _numberOfRows = 3;
+ _itemWidth = 88;
+ _itemHeight = 64;
+ _itemX = 8;
+ _itemY = 26;
+ _isLooping = true;
+}
+
+void InventoryItemsPicture::loopCurrentItem() {
+ if (_isLooping) {
+ CoordType x, y;
+ getItemXY(_currentItemIndex, x, y);
+ _panelMovie.moveMovieBoxTo(x, y);
+ _highlightBounds.moveTo(x, y);
+
+ TimeValue start, stop;
+ ((InventoryItem *)_currentItem)->getPanelTimes(start, stop);
+ _panelMovie.stop();
+ _panelMovie.setFlags(0);
+ _panelMovie.setSegment(start, stop);
+ _panelMovie.setFlags(kLoopTimeBase);
+ _panelMovie.setTime(((InventoryItem *)_currentItem)->getAnimationTime());
+ _panelMovie.start();
+ }
+}
+
+void InventoryItemsPicture::highlightCurrentItem() {
+ InventoryPicture::highlightCurrentItem();
+ loopCurrentItem();
+}
+
+void InventoryItemsPicture::unhighlightCurrentItem() {
+ InventoryPicture::unhighlightCurrentItem();
+ _panelMovie.stop();
+ _panelMovie.setFlags(0);
+ ((InventoryItem *)_currentItem)->setAnimationTime(_panelMovie.getTime());
+}
+
+TimeValue InventoryItemsPicture::getItemPanelTime(Item *item) {
+ TimeValue start, stop;
+ ((InventoryItem *)item)->getPanelTimes(start, stop);
+ ((InventoryItem *)item)->setAnimationTime(start);
+ return start;
+}
+
+void InventoryItemsPicture::deactivateInventoryPicture() {
+ if (_active) {
+ InventoryPicture::deactivateInventoryPicture();
+ _panelMovie.stop();
+ _panelMovie.setFlags(0);
+ _panelMovie.setSegment(0, _panelMovie.getDuration());
+ _isLooping = true;
+ }
+}
+
+void InventoryItemsPicture::playEndMessage(DisplayElement *pushElement) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ Movie endMessage(0);
+
+ _shouldDrawHighlight = false;
+ endMessage.shareSurface(this);
+ endMessage.initFromMovieFile("Images/Caldoria/A56 Congrats");
+ endMessage.moveMovieBoxTo(kFinalMessageLeft - kInventoryPushLeft, kFinalMessageTop - kInventoryPushTop);
+ endMessage.setTriggeredElement(pushElement);
+ endMessage.start();
+
+ while (endMessage.isRunning()) {
+ vm->checkCallBacks();
+ vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ endMessage.stop();
+}
+
+BiochipPicture::BiochipPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) :
+ InventoryPicture(id, nextHandler, inventory) {
+ _pictName = "Images/Items/Biochips/Biochip Panel";
+ _movieName = "Images/Items/Biochips/Biochip Panel Movie";
+ _highlightName = "Images/Items/Biochips/BioChip Hilite";
+
+ _itemsPerRow = 4;
+ _numberOfRows = 2;
+ _itemWidth = 46;
+ _itemHeight = 46;
+ _itemX = 4;
+ _itemY = 24;
+}
+
+void BiochipPicture::unhighlightCurrentItem() {
+ InventoryPicture::unhighlightCurrentItem();
+ CoordType x, y;
+ getItemXY(_currentItemIndex, x, y);
+ _panelMovie.show();
+ _panelMovie.moveMovieBoxTo(x, y);
+ _panelMovie.setTime(getItemPanelTime(_currentItem));
+ _panelMovie.redrawMovieWorld();
+}
+
+TimeValue BiochipPicture::getItemPanelTime(Item *item) {
+ return ((BiochipItem *)item)->getPanelTime();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/inventorypicture.h b/engines/pegasus/items/inventorypicture.h
new file mode 100644
index 0000000000..88a4a4ba75
--- /dev/null
+++ b/engines/pegasus/items/inventorypicture.h
@@ -0,0 +1,125 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_INVENTORYPICTURE_H
+#define PEGASUS_ITEMS_INVENTORYPICTURE_H
+
+#include "pegasus/input.h"
+#include "pegasus/movie.h"
+#include "pegasus/surface.h"
+
+namespace Pegasus {
+
+class Inventory;
+class Item;
+class Input;
+class Transition;
+
+class InventoryPicture : public InputHandler, public Picture {
+public:
+ InventoryPicture(const DisplayElementID, InputHandler *, Inventory *);
+ virtual ~InventoryPicture() {}
+
+ void initInventoryImage(Transition *);
+ void throwAwayInventoryImage();
+
+ void panelUp();
+ void activateInventoryPicture();
+ void deactivateInventoryPicture();
+ void handleInput(const Input &, const Hotspot *);
+ bool wantsCursor() { return false; }
+
+ InventoryResult addInventoryItem(Item *);
+ InventoryResult removeInventoryItem(Item *);
+ void removeAllItems();
+ Item *getCurrentItem() { return _currentItem; }
+ void setCurrentItemIndex(int32);
+ void setCurrentItemID(ItemID);
+ int32 getCurrentItemIndex() { return _currentItemIndex; }
+ bool itemInInventory(Item *);
+ bool itemInInventory(const ItemID);
+
+protected:
+ void getItemXY(uint32, CoordType &, CoordType &);
+ void draw(const Common::Rect &);
+ void drawItemHighlight(const Common::Rect &);
+ virtual void highlightCurrentItem();
+ virtual void unhighlightCurrentItem() {}
+ virtual TimeValue getItemPanelTime(Item *) = 0;
+
+ Inventory *_inventory;
+ uint32 _lastReferenceCount;
+ Frame _highlightImage;
+ Movie _panelMovie;
+ int32 _currentItemIndex;
+ Item *_currentItem;
+ Common::Rect _highlightBounds;
+ bool _active, _shouldDrawHighlight;
+
+ Common::String _pictName;
+ Common::String _movieName;
+ Common::String _highlightName;
+ uint16 _itemsPerRow;
+ uint16 _numberOfRows;
+ uint16 _itemWidth;
+ uint16 _itemHeight;
+ uint16 _itemX;
+ uint16 _itemY;
+};
+
+class InventoryItemsPicture : public InventoryPicture {
+public:
+ InventoryItemsPicture(const DisplayElementID, InputHandler *, Inventory *);
+ virtual ~InventoryItemsPicture() {}
+
+ void deactivateInventoryPicture();
+
+ void disableLooping() { _isLooping = false; }
+
+ void playEndMessage(DisplayElement *);
+
+protected:
+ virtual void highlightCurrentItem();
+ virtual void unhighlightCurrentItem();
+ virtual TimeValue getItemPanelTime(Item *);
+ void loopCurrentItem();
+
+ InputHandler *_previousHandler;
+ bool _isLooping;
+};
+
+class BiochipPicture : public InventoryPicture {
+public:
+ BiochipPicture(const DisplayElementID, InputHandler *, Inventory *);
+ virtual ~BiochipPicture() {}
+
+protected:
+ virtual void unhighlightCurrentItem();
+ virtual TimeValue getItemPanelTime(Item *);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/item.cpp b/engines/pegasus/items/item.cpp
new file mode 100644
index 0000000000..8089f2b93d
--- /dev/null
+++ b/engines/pegasus/items/item.cpp
@@ -0,0 +1,314 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/error.h"
+#include "common/stream.h"
+
+#include "pegasus/constants.h"
+#include "pegasus/elements.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/surface.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/item.h"
+#include "pegasus/items/itemlist.h"
+#include "pegasus/items/biochips/biochipitem.h"
+#include "pegasus/items/inventory/inventoryitem.h"
+
+namespace Pegasus {
+
+Item::Item(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : IDObject(id) {
+ _itemNeighborhood = neighborhood;
+ _itemRoom = room;
+ _itemDirection = direction;
+ _itemWeight = 1;
+ _itemOwnerID = kNoActorID;
+ _itemState = 0;
+
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ Common::SeekableReadStream *info = vm->_resFork->getResource(kItemInfoResType, kItemBaseResID + id);
+ if (info) {
+ _itemInfo.infoLeftTime = info->readUint32BE();
+ _itemInfo.infoRightStart = info->readUint32BE();
+ _itemInfo.infoRightStop = info->readUint32BE();
+ _itemInfo.dragSpriteNormalID = info->readUint16BE();
+ _itemInfo.dragSpriteUsedID = info->readUint16BE();
+
+ if (vm->isDemo()) {
+ // Adjust info right times to account for the stuff that was chopped out of the
+ // info right movies.
+ // Assumes time scale of 600.
+
+ // Gap times in seconds
+ static const TimeValue kGap1 = 24;
+ static const TimeValue kGap2 = 34;
+ static const TimeValue kGap3 = 4;
+ static const TimeValue kGap4 = 4;
+
+ static const TimeValue kGapForGroup1 = kGap1;
+ static const TimeValue kGapForGroup2 = kGapForGroup1 + kGap2;
+ static const TimeValue kGapForGroup3 = kGapForGroup2 + kGap3;
+ static const TimeValue kGapForGroup4 = kGapForGroup3 + kGap4;
+
+ switch (id) {
+ case kHistoricalLog:
+ case kJourneymanKey:
+ case kKeyCard:
+ _itemInfo.infoRightStart -= 600 * kGapForGroup1;
+ _itemInfo.infoRightStop -= 600 * kGapForGroup1;
+ break;
+ case kAIBiochip:
+ _itemInfo.infoRightStart -= 600 * kGapForGroup2;
+ _itemInfo.infoRightStop -= 600 * kGapForGroup2;
+ break;
+ case kMapBiochip:
+ _itemInfo.infoRightStart -= 600 * kGapForGroup3;
+ _itemInfo.infoRightStop -= 600 * kGapForGroup3;
+ break;
+ case kPegasusBiochip:
+ _itemInfo.infoRightStart -= 600 * kGapForGroup4;
+ _itemInfo.infoRightStop -= 600 * kGapForGroup4;
+ break;
+ }
+ }
+
+ delete info;
+ } else {
+ memset(&_itemInfo, 0, sizeof(_itemInfo));
+ }
+
+ Common::SeekableReadStream *middleAreaInfo = vm->_resFork->getResource(kMiddleAreaInfoResType, kItemBaseResID + id);
+ if (middleAreaInfo) {
+ _sharedAreaInfo = readItemState(middleAreaInfo);
+ delete middleAreaInfo;
+ } else {
+ // Only kArgonPickup does not have this
+ memset(&_sharedAreaInfo, 0, sizeof(_sharedAreaInfo));
+ }
+
+ Common::SeekableReadStream *extraInfo = vm->_resFork->getResource(kItemExtraInfoResType, kItemBaseResID + id);
+ if (!extraInfo)
+ error("Extra info not found for item %d", id);
+
+ _itemExtras.numEntries = extraInfo->readUint16BE();
+ _itemExtras.entries = new ItemExtraEntry[_itemExtras.numEntries];
+ for (uint16 i = 0; i < _itemExtras.numEntries; i++) {
+ _itemExtras.entries[i].extraID = extraInfo->readUint32BE();
+ _itemExtras.entries[i].extraArea = extraInfo->readUint16BE();
+ _itemExtras.entries[i].extraStart = extraInfo->readUint32BE();
+ _itemExtras.entries[i].extraStop = extraInfo->readUint32BE();
+ }
+
+ delete extraInfo;
+
+ g_allItems.push_back(this);
+}
+
+Item::~Item() {
+ delete[] _sharedAreaInfo.entries;
+ delete[] _itemExtras.entries;
+}
+
+void Item::writeToStream(Common::WriteStream *stream) {
+ stream->writeUint16BE(_itemNeighborhood);
+ stream->writeUint16BE(_itemRoom);
+ stream->writeByte(_itemDirection);
+ stream->writeUint16BE(_itemOwnerID);
+ stream->writeUint16BE(_itemState);
+}
+
+void Item::readFromStream(Common::ReadStream *stream) {
+ _itemNeighborhood = stream->readUint16BE();
+ _itemRoom = stream->readUint16BE();
+ _itemDirection = stream->readByte();
+ _itemOwnerID = stream->readUint16BE();
+ _itemState = stream->readUint16BE();
+}
+
+ActorID Item::getItemOwner() const {
+ return _itemOwnerID;
+}
+
+void Item::setItemOwner(const ActorID owner) {
+ _itemOwnerID = owner;
+
+ if (owner == kNoActorID) {
+ if (isSelected())
+ deselect();
+ removedFromInventory();
+ } else {
+ addedToInventory();
+ }
+}
+
+WeightType Item::getItemWeight() {
+ return _itemWeight;
+}
+
+ItemState Item::getItemState() const {
+ return _itemState;
+}
+
+void Item::setItemState(const ItemState state) {
+ if (state != _itemState) {
+ _itemState = state;
+
+ if (getItemType() == kInventoryItemType && ((PegasusEngine *)g_engine)->getCurrentInventoryItem() == (InventoryItem *)this)
+ select();
+ else if (getItemType() == kBiochipItemType && ((PegasusEngine *)g_engine)->getCurrentBiochip() == (BiochipItem *)this)
+ select();
+ }
+}
+
+void Item::getItemRoom(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) const {
+ neighborhood = _itemNeighborhood;
+ room = _itemRoom;
+ direction = _itemDirection;
+}
+
+void Item::setItemRoom(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) {
+ _itemNeighborhood = neighborhood;
+ _itemRoom = room;
+ _itemDirection = direction;
+
+ if (neighborhood == kNoNeighborhoodID)
+ pickedUp();
+ else
+ dropped();
+}
+
+NeighborhoodID Item::getItemNeighborhood() const {
+ return _itemNeighborhood;
+}
+
+TimeValue Item::getSharedAreaTime() const {
+ if (!_sharedAreaInfo.entries)
+ return 0xffffffff;
+
+ TimeValue time;
+ ItemState state;
+
+ findItemStateEntryByState(_sharedAreaInfo, _itemState, time);
+ if (time == 0xffffffff)
+ getItemStateEntry(_sharedAreaInfo, 0, state, time);
+
+ return time;
+}
+
+// Must affect images in shared area.
+void Item::select() {
+ _isSelected = true;
+
+ if (g_AIArea) {
+ if (getItemType() == kInventoryItemType)
+ g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, getSharedAreaTime());
+ else
+ g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, getSharedAreaTime());
+ }
+}
+
+void Item::deselect() {
+ _isActive = false;
+ _isSelected = false;
+
+ if (g_AIArea) {
+ if (getItemType() == kInventoryItemType)
+ g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, 0xffffffff);
+ else
+ g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, 0xffffffff);
+ }
+}
+
+void Item::getItemStateEntry(ItemStateInfo info, uint32 index, ItemState &state, TimeValue &time) {
+ if (index < info.numEntries) {
+ state = info.entries[index].itemState;
+ time = info.entries[index].itemTime;
+ } else {
+ state = kNoItemState;
+ time = 0xffffffff;
+ }
+}
+
+void Item::findItemStateEntryByState(ItemStateInfo info, ItemState state, TimeValue &time) {
+ for (uint16 i = 0; i < info.numEntries; i++) {
+ if (info.entries[i].itemState == state) {
+ time = info.entries[i].itemTime;
+ return;
+ }
+ }
+
+ time = 0xffffffff;
+}
+
+ItemStateInfo Item::readItemState(Common::SeekableReadStream *stream) {
+ ItemStateInfo info;
+
+ info.numEntries = stream->readUint16BE();
+ info.entries = new ItemStateEntry[info.numEntries];
+ for (uint16 i = 0; i < info.numEntries; i++) {
+ info.entries[i].itemState = stream->readSint16BE();
+ info.entries[i].itemTime = stream->readUint32BE();
+ }
+
+ return info;
+}
+
+Sprite *Item::getDragSprite(const DisplayElementID id) const {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+ Sprite *result = new Sprite(id);
+ SpriteFrame *frame = new SpriteFrame();
+
+ frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteNormalID, true);
+ result->addFrame(frame, 0, 0);
+
+ if (_itemInfo.dragSpriteNormalID != _itemInfo.dragSpriteUsedID) {
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteUsedID, true);
+ }
+
+ result->addFrame(frame, 0, 0);
+ result->setCurrentFrameIndex(0);
+ return result;
+}
+
+TimeValue Item::getInfoLeftTime() const {
+ return _itemInfo.infoLeftTime;
+}
+
+void Item::getInfoRightTimes(TimeValue &start, TimeValue &stop) const {
+ start = _itemInfo.infoRightStart;
+ stop = _itemInfo.infoRightStop;
+}
+
+void Item::findItemExtra(const uint32 extraID, ItemExtraEntry &entry) {
+ for (uint32 i = 0; i < _itemExtras.numEntries; i++) {
+ if (_itemExtras.entries[i].extraID == extraID) {
+ entry = _itemExtras.entries[i];
+ return;
+ }
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/item.h b/engines/pegasus/items/item.h
new file mode 100644
index 0000000000..a1451b2a58
--- /dev/null
+++ b/engines/pegasus/items/item.h
@@ -0,0 +1,363 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_ITEM_H
+#define PEGASUS_ITEMS_ITEM_H
+
+#include "common/endian.h"
+
+#include "pegasus/types.h"
+#include "pegasus/util.h"
+
+namespace Common {
+ class Error;
+ class ReadStream;
+ class WriteStream;
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+// JMPItemInfo contains resource data used by all Items.
+
+struct JMPItemInfo {
+ TimeValue infoLeftTime;
+ TimeValue infoRightStart;
+ TimeValue infoRightStop;
+ uint32 dragSpriteNormalID;
+ uint32 dragSpriteUsedID;
+};
+
+// ItemStateEntry contains a single state/TimeValue pair. The TimeValue is
+// the time value to set the shared area movie that corresponds with the given
+// state of an inventory item.
+
+struct ItemStateEntry {
+ ItemState itemState;
+ TimeValue itemTime;
+};
+
+struct ItemStateInfo {
+ uint16 numEntries; // For easy ResEdit access
+ ItemStateEntry *entries;
+};
+
+// ItemExtraEntry
+
+static const short kLeftAreaExtra = 0;
+static const short kMiddleAreaExtra = 1;
+static const short kRightAreaExtra = 2;
+
+struct ItemExtraEntry {
+ uint32 extraID;
+ uint16 extraArea;
+ TimeValue extraStart;
+ TimeValue extraStop;
+};
+
+struct ItemExtraInfo {
+ uint16 numEntries; // For easy ResEdit access
+ ItemExtraEntry *entries;
+};
+
+// Inventory info resource type and ID:
+// Individual inventory items are stored in these resource types.
+// Resource ID is item ID + kItemBaseResID.
+
+static const uint32 kItemInfoResType = MKTAG('I', 't', 'e', 'm'); // JMPItemInfoHandle
+static const uint32 kLeftAreaInfoResType = MKTAG('L', 'e', 'f', 't'); // ItemStateInfoHandle
+static const uint32 kMiddleAreaInfoResType = MKTAG('M', 'i', 'd', 'l'); // ItemStateInfoHandle
+static const uint32 kRightAreaInfoResType = MKTAG('R', 'g', 'h', 't'); // ItemStateInfoHandle
+static const uint32 kItemExtraInfoResType = MKTAG('I', 'X', 't', 'r'); // ItemExtraInfoHandle
+
+static const uint16 kItemBaseResID = 128;
+
+// Item IDs.
+
+static const ItemID kAirMask = 7;
+static const ItemID kAntidote = 8;
+static const ItemID kArgonCanister = 9;
+static const ItemID kCardBomb = 10;
+static const ItemID kCrowbar = 11;
+static const ItemID kGasCanister = 12;
+static const ItemID kHistoricalLog = 13;
+static const ItemID kJourneymanKey = 14;
+static const ItemID kKeyCard = 15;
+static const ItemID kMachineGun = 16;
+static const ItemID kMarsCard = 17;
+static const ItemID kNitrogenCanister = 18;
+static const ItemID kOrangeJuiceGlassFull = 19;
+static const ItemID kOrangeJuiceGlassEmpty = 20;
+static const ItemID kPoisonDart = 21;
+static const ItemID kSinclairKey = 22;
+static const ItemID kStunGun = 23;
+static const ItemID kArgonPickup = 24;
+
+// Biochips.
+
+static const ItemID kAIBiochip = 0;
+static const ItemID kInterfaceBiochip = 1;
+static const ItemID kMapBiochip = 2;
+static const ItemID kOpticalBiochip = 3;
+static const ItemID kPegasusBiochip = 4;
+static const ItemID kRetinalScanBiochip = 5;
+static const ItemID kShieldBiochip = 6;
+
+static const ItemID kNumItems = 25;
+
+// Item States.
+
+static const ItemState kAI000 = 0;
+static const ItemState kAI005 = 1;
+static const ItemState kAI006 = 2;
+static const ItemState kAI010 = 3;
+static const ItemState kAI015 = 4;
+static const ItemState kAI016 = 5;
+static const ItemState kAI020 = 6;
+static const ItemState kAI024 = 7;
+static const ItemState kAI100 = 8;
+static const ItemState kAI101 = 9;
+static const ItemState kAI105 = 10;
+static const ItemState kAI106 = 11;
+static const ItemState kAI110 = 12;
+static const ItemState kAI111 = 13;
+static const ItemState kAI115 = 14;
+static const ItemState kAI116 = 15;
+static const ItemState kAI120 = 16;
+static const ItemState kAI121 = 17;
+static const ItemState kAI124 = 18;
+static const ItemState kAI125 = 19;
+static const ItemState kAI126 = 20;
+static const ItemState kAI200 = 21;
+static const ItemState kAI201 = 22;
+static const ItemState kAI202 = 23;
+static const ItemState kAI205 = 24;
+static const ItemState kAI206 = 25;
+static const ItemState kAI210 = 26;
+static const ItemState kAI211 = 27;
+static const ItemState kAI212 = 28;
+static const ItemState kAI215 = 29;
+static const ItemState kAI216 = 30;
+static const ItemState kAI220 = 31;
+static const ItemState kAI221 = 32;
+static const ItemState kAI222 = 33;
+static const ItemState kAI224 = 34;
+static const ItemState kAI225 = 35;
+static const ItemState kAI226 = 36;
+static const ItemState kAI300 = 37;
+static const ItemState kAI301 = 38;
+static const ItemState kAI302 = 39;
+static const ItemState kAI303 = 40;
+static const ItemState kAI305 = 41;
+static const ItemState kAI306 = 42;
+static const ItemState kAI310 = 43;
+static const ItemState kAI311 = 44;
+static const ItemState kAI312 = 45;
+static const ItemState kAI313 = 46;
+static const ItemState kAI315 = 47;
+static const ItemState kAI316 = 48;
+static const ItemState kAI320 = 49;
+static const ItemState kAI321 = 50;
+static const ItemState kAI322 = 51;
+static const ItemState kAI323 = 52;
+static const ItemState kAI324 = 53;
+static const ItemState kAI325 = 54;
+static const ItemState kAI326 = 55;
+static const ItemState kNormalItem = 56;
+static const ItemState kMapUnavailable = 57;
+static const ItemState kMapEngaged = 58;
+static const ItemState kOptical000 = 59;
+static const ItemState kOptical001 = 60;
+static const ItemState kOptical002 = 61;
+static const ItemState kOptical010 = 62;
+static const ItemState kOptical011 = 63;
+static const ItemState kOptical012 = 64;
+static const ItemState kOptical020 = 65;
+static const ItemState kOptical021 = 66;
+static const ItemState kOptical100 = 67;
+static const ItemState kOptical101 = 68;
+static const ItemState kOptical102 = 69;
+static const ItemState kOptical110 = 70;
+static const ItemState kOptical111 = 71;
+static const ItemState kOptical112 = 72;
+static const ItemState kOptical120 = 73;
+static const ItemState kOptical121 = 74;
+static const ItemState kOptical200 = 75;
+static const ItemState kOptical201 = 76;
+static const ItemState kOptical210 = 77;
+static const ItemState kOptical211 = 78;
+static const ItemState kPegasusTSA00 = 79;
+static const ItemState kPegasusTSA10 = 80;
+static const ItemState kPegasusPrehistoric00 = 81;
+static const ItemState kPegasusPrehistoric01 = 82;
+static const ItemState kPegasusPrehistoric10 = 83;
+static const ItemState kPegasusPrehistoric11 = 84;
+static const ItemState kPegasusMars00 = 85;
+static const ItemState kPegasusMars01 = 86;
+static const ItemState kPegasusMars10 = 87;
+static const ItemState kPegasusMars11 = 88;
+static const ItemState kPegasusNorad00 = 89;
+static const ItemState kPegasusNorad01 = 90;
+static const ItemState kPegasusNorad10 = 91;
+static const ItemState kPegasusNorad11 = 92;
+static const ItemState kPegasusWSC00 = 93;
+static const ItemState kPegasusWSC01 = 94;
+static const ItemState kPegasusWSC10 = 95;
+static const ItemState kPegasusWSC11 = 96;
+static const ItemState kPegasusCaldoria = 97;
+static const ItemState kRetinalSimulating = 98;
+static const ItemState kShieldNormal = 99;
+static const ItemState kShieldRadiation = 100;
+static const ItemState kShieldPlasma = 101;
+static const ItemState kShieldCardBomb = 102;
+static const ItemState kShieldDraining = 103;
+static const ItemState kAirMaskEmptyOff = 104;
+static const ItemState kAirMaskEmptyFilter = 105;
+static const ItemState kAirMaskLowOff = 106;
+static const ItemState kAirMaskLowFilter = 107;
+static const ItemState kAirMaskLowOn = 108;
+static const ItemState kAirMaskFullOff = 109;
+static const ItemState kAirMaskFullFilter = 110;
+static const ItemState kAirMaskFullOn = 111;
+static const ItemState kArgonEmpty = 112;
+static const ItemState kArgonFull = 113;
+static const ItemState kFlashlightOff = 114;
+static const ItemState kFlashlightOn = 115;
+static const ItemState kNitrogenEmpty = 116;
+static const ItemState kNitrogenFull = 117;
+static const ItemState kFullGlass = 118;
+
+// Extra IDs.
+
+static const uint32 kRetinalScanSearching = 0;
+static const uint32 kRetinalScanActivated = 1;
+static const uint32 kShieldIntro = 2;
+static const uint32 kRemoveAirMask = 3;
+static const uint32 kRemoveArgon = 4;
+static const uint32 kRemoveCrowbar = 5;
+static const uint32 kGasCanLoop = 6;
+static const uint32 kRemoveJourneymanKey = 7;
+static const uint32 kRemoveMarsCard = 8;
+static const uint32 kRemoveNitrogen = 9;
+static const uint32 kRemoveGlass = 10;
+static const uint32 kRemoveDart = 11;
+static const uint32 kRemoveSinclairKey = 12;
+
+enum ItemType {
+ kInventoryItemType,
+ kBiochipItemType
+};
+
+class Sprite;
+
+/*
+
+ Item is an object which can be picked up and carried around.
+ Items have
+ a location
+ an ID.
+ weight
+ an owner (kNoActorID if no one is carrying the Item)
+
+*/
+
+class Item : public IDObject {
+public:
+ Item(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction);
+ virtual ~Item();
+
+ // WriteToStream writes everything EXCEPT the item's ID.
+ // It is assumed that the calling function will write and read the ID.
+ virtual void writeToStream(Common::WriteStream *stream);
+ virtual void readFromStream(Common::ReadStream *stream);
+
+ virtual ActorID getItemOwner() const;
+ virtual void setItemOwner(const ActorID owner);
+
+ void getItemRoom(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) const;
+ void setItemRoom(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction);
+ NeighborhoodID getItemNeighborhood() const;
+
+ virtual WeightType getItemWeight();
+
+ virtual void setItemState(const ItemState state);
+ virtual ItemState getItemState() const;
+
+ virtual ItemType getItemType() = 0;
+
+ TimeValue getInfoLeftTime() const;
+ void getInfoRightTimes(TimeValue &, TimeValue &) const;
+ TimeValue getSharedAreaTime() const;
+
+ Sprite *getDragSprite(const DisplayElementID) const;
+
+ /*
+ select -- called when this item becomes current. Also called when the inventory
+ panel holding this item is raised and this is the current item.
+ deselect -- called when this item is no longer current.
+ activate -- called on the current item when the panel is closed.
+ */
+ // In an override of these three member functions, you must call the inherited
+ // member functions.
+ virtual void select();
+ virtual void deselect();
+ virtual bool isSelected() { return _isSelected; }
+
+ virtual void activate() { _isActive = true; }
+ virtual bool isActive() { return _isActive; }
+ virtual void pickedUp() {}
+ virtual void addedToInventory() {}
+ virtual void removedFromInventory() {}
+ virtual void dropped() {}
+
+ // Called when the shared area is taken by another item, but this item is still
+ // selected.
+ virtual void giveUpSharedArea() {}
+ virtual void takeSharedArea() {}
+
+ void findItemExtra(const uint32 extraID, ItemExtraEntry &entry);
+
+protected:
+ NeighborhoodID _itemNeighborhood;
+ RoomID _itemRoom;
+ DirectionConstant _itemDirection;
+ ActorID _itemOwnerID;
+ WeightType _itemWeight;
+ ItemState _itemState;
+
+ JMPItemInfo _itemInfo;
+ ItemStateInfo _sharedAreaInfo;
+ ItemExtraInfo _itemExtras;
+ bool _isActive;
+ bool _isSelected;
+
+ static void getItemStateEntry(ItemStateInfo, uint32, ItemState &, TimeValue &);
+ static void findItemStateEntryByState(ItemStateInfo, ItemState, TimeValue &);
+ static ItemStateInfo readItemState(Common::SeekableReadStream *stream);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/itemdragger.cpp b/engines/pegasus/items/itemdragger.cpp
new file mode 100644
index 0000000000..97fc5a97ac
--- /dev/null
+++ b/engines/pegasus/items/itemdragger.cpp
@@ -0,0 +1,193 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/elements.h"
+#include "pegasus/hotspot.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/items/itemdragger.h"
+
+namespace Pegasus {
+
+SpriteDragger::SpriteDragger() {
+ _draggingSprite = 0;
+ _limitRect = Common::Rect(-30000, -30000, 30000, 30000);
+ _slopRect = Common::Rect(-30000, -30000, 30000, 30000);
+ _dragOffset.x = 0;
+ _dragOffset.y = 0;
+ _lastHotspot = 0;
+}
+
+void SpriteDragger::setDragSprite(Sprite *newSprite) {
+ if (!isTracking())
+ _draggingSprite = newSprite;
+}
+
+void SpriteDragger::setDragConstraints(const Common::Rect &limitRect, const Common::Rect &slopRect) {
+ if (!isTracking()) {
+ _rawLimitRect = limitRect;
+ _slopRect = slopRect;
+ }
+}
+
+void SpriteDragger::getDragConstraints(Common::Rect &limitRect, Common::Rect &slopRect) const {
+ limitRect = _rawLimitRect;
+ slopRect = _slopRect;
+}
+
+void SpriteDragger::startTracking(const Input &input) {
+ if (_draggingSprite) {
+ Tracker::startTracking(input);
+
+ if (isTracking()) {
+ input.getInputLocation(_startPoint);
+ _lastRawPoint = _startRawPoint = _startPoint;
+
+ Common::Rect r;
+ _draggingSprite->getBounds(r);
+ _dragOffset.x = _startPoint.x - r.left;
+ _dragOffset.y = _startPoint.y - r.top;
+
+ _limitRect = _rawLimitRect;
+ _limitRect.left += _dragOffset.x;
+ _limitRect.top += _dragOffset.y;
+ _limitRect.right -= r.width() - _dragOffset.x;
+ _limitRect.bottom -= r.height() - _dragOffset.y;
+ pinPointInRect(_limitRect, _startPoint);
+
+ _lastPoint = _startPoint;
+ if (_startPoint != _startRawPoint) {
+ Common::Point pt = _startPoint - _dragOffset;
+ _draggingSprite->moveElementTo(pt.x, pt.y);
+ }
+
+ _lastHotspot = g_allHotspots.findHotspot(_lastRawPoint);
+ if (_lastHotspot)
+ enterHotspot(_lastHotspot);
+ }
+ }
+}
+
+void SpriteDragger::continueTracking(const Input &input) {
+ if (_draggingSprite) {
+ Common::Point rawPoint;
+ input.getInputLocation(rawPoint);
+
+ if (!_slopRect.contains(rawPoint))
+ rawPoint = _startRawPoint;
+
+ if (rawPoint != _lastRawPoint) {
+ Common::Point newPoint = rawPoint;
+ pinPointInRect(_limitRect, newPoint);
+ newPoint -= _dragOffset;
+
+ if (newPoint != _lastPoint) {
+ _draggingSprite->moveElementTo(newPoint.x, newPoint.y);
+ _lastPoint = newPoint;
+ }
+
+ Hotspot *newHotspot = g_allHotspots.findHotspot(rawPoint);
+ if (newHotspot != _lastHotspot) {
+ if (_lastHotspot)
+ exitHotspot(_lastHotspot);
+ if (newHotspot)
+ enterHotspot(newHotspot);
+ _lastHotspot = newHotspot;
+ }
+
+ _lastRawPoint = rawPoint;
+ }
+ }
+}
+
+void SpriteDragger::pinPointInRect(const Common::Rect &r, Common::Point &pt) {
+ pt.x = CLIP<int>(pt.x, r.left, r.right - 1);
+ pt.y = CLIP<int>(pt.y, r.top, r.bottom - 1);
+}
+
+ItemDragger::ItemDragger(PegasusEngine *owner) : _inventoryDropSpot(kInventoryDropSpotID), _biochipDropSpot(kBiochipDropSpotID),
+ _inventoryHighlight(kInventoryDropHighlightID), _biochipHighlight(kBiochipDropHighlightID) {
+ _owner = owner;
+
+ Common::Rect r(kInventoryDropLeft, kInventoryDropTop, kInventoryDropRight, kInventoryDropBottom);
+ _inventoryDropSpot.setArea(r);
+ _inventoryDropSpot.setHotspotFlags(kDropItemSpotFlag);
+ g_allHotspots.push_back(&_inventoryDropSpot);
+
+ r = Common::Rect(kBiochipDropLeft, kBiochipDropTop, kBiochipDropRight, kBiochipDropBottom);
+ _biochipDropSpot.setArea(r);
+ _biochipDropSpot.setHotspotFlags(kDropBiochipSpotFlag);
+ g_allHotspots.push_back(&_biochipDropSpot);
+}
+
+void ItemDragger::startTracking(const Input &input) {
+ _inventoryHighlight.setDisplayOrder(kInventoryHiliteOrder);
+ _inventoryHighlight.startDisplaying();
+
+ _biochipHighlight.setDisplayOrder(kBiochipHiliteOrder);
+ _biochipHighlight.startDisplaying();
+
+ SpriteDragger::startTracking(input);
+}
+
+void ItemDragger::stopTracking(const Input &input) {
+ SpriteDragger::stopTracking(input);
+ _inventoryHighlight.hide();
+ _biochipHighlight.hide();
+ _inventoryHighlight.stopDisplaying();
+ _biochipHighlight.stopDisplaying();
+ _owner->dragTerminated(input);
+}
+
+bool ItemDragger::stopTrackingInput(const Input &input) {
+ return !JMPPPInput::isDraggingInput(input);
+}
+
+void ItemDragger::enterHotspot(Hotspot *spot) {
+ if (spot->getObjectID() == kInventoryDropSpotID)
+ _inventoryHighlight.show();
+ else if (spot->getObjectID() == kBiochipDropSpotID)
+ _biochipHighlight.show();
+ else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0)
+ _draggingSprite->setCurrentFrameIndex(1);
+}
+
+void ItemDragger::exitHotspot(Hotspot *spot) {
+ if (spot->getObjectID() == kInventoryDropSpotID)
+ _inventoryHighlight.hide();
+ else if (spot->getObjectID() == kBiochipDropSpotID)
+ _biochipHighlight.hide();
+ else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0)
+ _draggingSprite->setCurrentFrameIndex(0);
+}
+
+void ItemDragger::setHighlightBounds() {
+ uint32 color = g_system->getScreenFormat().RGBToColor(0x48, 0x80, 0xD8);
+ _inventoryHighlight.setBounds(Common::Rect(76, 334, 172, 430));
+ _inventoryHighlight.setHighlightColor(color);
+ _biochipHighlight.setBounds(Common::Rect(364, 334, 460, 430));
+ _biochipHighlight.setHighlightColor(color);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/itemdragger.h b/engines/pegasus/items/itemdragger.h
new file mode 100644
index 0000000000..fce953d695
--- /dev/null
+++ b/engines/pegasus/items/itemdragger.h
@@ -0,0 +1,96 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_ITEMDRAGGER_H
+#define PEGASUS_ITEMS_ITEMDRAGGER_H
+
+#include "pegasus/elements.h"
+#include "pegasus/input.h"
+
+namespace Pegasus {
+
+// TODO: Merge SpriteDragger and ItemDragger
+
+class Hotspot;
+class Sprite;
+
+class SpriteDragger : public Tracker {
+public:
+ SpriteDragger();
+ virtual ~SpriteDragger() {}
+
+ void setDragSprite(Sprite *);
+ Sprite *getDragSprite() { return _draggingSprite; }
+
+ void setDragConstraints(const Common::Rect &, const Common::Rect &);
+ void getDragConstraints(Common::Rect &, Common::Rect &) const;
+
+ void startTracking(const Input &);
+ void continueTracking(const Input&);
+
+ Hotspot *getLastHotspot() const { return _lastHotspot; }
+
+protected:
+ virtual void enterHotspot(Hotspot *) {}
+ virtual void exitHotspot(Hotspot *) {}
+
+ Sprite *_draggingSprite;
+ Common::Point _startPoint, _lastPoint, _dragOffset;
+ Common::Point _startRawPoint, _lastRawPoint;
+ Common::Rect _rawLimitRect;
+ Common::Rect _limitRect;
+ Common::Rect _slopRect;
+ Hotspot *_lastHotspot;
+
+ // This is a replica of QuickDraw's PinPointInRect function
+ void pinPointInRect(const Common::Rect &, Common::Point &);
+};
+
+class PegasusEngine;
+
+class ItemDragger : public SpriteDragger {
+public:
+ ItemDragger(PegasusEngine *);
+ virtual ~ItemDragger() {}
+
+ void setHighlightBounds();
+ void startTracking(const Input &);
+ void stopTracking(const Input &);
+ bool stopTrackingInput(const Input &);
+
+protected:
+ virtual void enterHotspot(Hotspot *);
+ virtual void exitHotspot(Hotspot *);
+
+ PegasusEngine *_owner;
+ DropHighlight _inventoryHighlight;
+ Hotspot _inventoryDropSpot;
+ DropHighlight _biochipHighlight;
+ Hotspot _biochipDropSpot;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/items/itemlist.cpp b/engines/pegasus/items/itemlist.cpp
new file mode 100644
index 0000000000..ff8cae546b
--- /dev/null
+++ b/engines/pegasus/items/itemlist.cpp
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/error.h"
+#include "common/stream.h"
+
+#include "pegasus/pegasus.h"
+#include "pegasus/items/item.h"
+#include "pegasus/items/itemlist.h"
+
+namespace Pegasus {
+
+ItemList::ItemList() {
+}
+
+ItemList::~ItemList() {
+}
+
+void ItemList::writeToStream(Common::WriteStream *stream) {
+ stream->writeUint32BE(size());
+
+ for (ItemIterator it = begin(); it != end(); it++) {
+ stream->writeUint16BE((*it)->getObjectID());
+ (*it)->writeToStream(stream);
+ }
+}
+
+void ItemList::readFromStream(Common::ReadStream *stream) {
+ uint32 itemCount = stream->readUint32BE();
+
+ for (uint32 i = 0; i < itemCount; i++) {
+ ItemID itemID = stream->readUint16BE();
+ g_allItems.findItemByID(itemID)->readFromStream(stream);
+ }
+}
+
+Item *ItemList::findItemByID(const ItemID id) {
+ for (ItemIterator it = begin(); it != end(); it++)
+ if ((*it)->getObjectID() == id)
+ return *it;
+
+ return 0;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/itemlist.h b/engines/pegasus/items/itemlist.h
new file mode 100644
index 0000000000..9b59206ab3
--- /dev/null
+++ b/engines/pegasus/items/itemlist.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_ITEMLIST_H
+#define PEGASUS_ITEMS_ITEMLIST_H
+
+#include "common/list.h"
+
+#include "pegasus/types.h"
+
+namespace Common {
+ class ReadStream;
+ class WriteStream;
+}
+
+namespace Pegasus {
+
+class Item;
+
+class ItemList : public Common::List<Item *> {
+public:
+ ItemList();
+ virtual ~ItemList();
+
+ virtual void writeToStream(Common::WriteStream *stream);
+ virtual void readFromStream(Common::ReadStream *stream);
+
+ Item *findItemByID(const ItemID id);
+};
+
+typedef ItemList::iterator ItemIterator;
+
+#define g_allItems (((PegasusEngine *)g_engine)->getAllItems())
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/menu.cpp b/engines/pegasus/menu.cpp
new file mode 100644
index 0000000000..deaa460188
--- /dev/null
+++ b/engines/pegasus/menu.cpp
@@ -0,0 +1,1219 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/menu.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/scoring.h"
+
+namespace Pegasus {
+
+GameMenu::GameMenu(const uint32 id) : IDObject(id), InputHandler((InputHandler *)((PegasusEngine *)g_engine)) {
+ _previousHandler = 0;
+ _lastCommand = kMenuCmdNoCommand;
+}
+
+void GameMenu::becomeCurrentHandler() {
+ _previousHandler = InputHandler::setInputHandler(this);
+}
+
+void GameMenu::restorePreviousHandler() {
+ InputHandler::setInputHandler(_previousHandler);
+}
+
+void GameMenu::drawScore(GameScoreType score, GameScoreType total, const Common::Rect &scoreBounds, Surface *numbers) {
+ CoordType x = scoreBounds.right;
+ drawNumber(total, x, scoreBounds.top, numbers);
+
+ x -= 12;
+ Common::Rect r1(120, 0, 132, 12); // The slash.
+ Common::Rect r2 = r1;
+ r2.moveTo(x, scoreBounds.top);
+ numbers->copyToCurrentPort(r1, r2);
+ drawNumber(score, x, scoreBounds.top, numbers);
+}
+
+void GameMenu::drawNumber(GameScoreType number, CoordType &x, CoordType y, Surface *numbers) {
+ Common::Rect r1(0, 0, 12, 12); // Width, height of one digit
+ Common::Rect r2 = r1;
+ r2.moveTo(x - 12, y);
+
+ do {
+ uint16 digit = number % 10;
+ number /= 10;
+ r1.moveTo(digit * 12, 0);
+ numbers->copyToCurrentPort(r1, r2);
+ r2.translate(-12, 0);
+ } while (number != 0);
+
+ x = r2.right;
+}
+
+enum {
+ kMainMenuStartDemo = 0,
+ kMainMenuCreditsDemo,
+ kMainMenuQuitDemo,
+ kFirstSelectionDemo = kMainMenuStartDemo,
+ kLastSelectionDemo = kMainMenuQuitDemo,
+
+ kMainMenuOverview = 0,
+ kMainMenuStart,
+ kMainMenuRestore,
+ kMainMenuDifficulty,
+ kMainMenuCredits,
+ kMainMenuQuit,
+ kFirstSelection = kMainMenuOverview,
+ kLastSelection = kMainMenuQuit
+};
+
+static const CoordType kStartLeftDemo = 44;
+static const CoordType kStartTopDemo = 336;
+
+static const CoordType kStartSelectLeftDemo = 40;
+static const CoordType kStartSelectTopDemo = 331;
+
+static const CoordType kCreditsLeftDemo = 44;
+static const CoordType kCreditsTopDemo = 372;
+
+static const CoordType kCreditsSelectLeftDemo = 40;
+static const CoordType kCreditsSelectTopDemo = 367;
+
+static const CoordType kMainMenuQuitLeftDemo = 32;
+static const CoordType kMainMenuQuitTopDemo = 412;
+
+static const CoordType kMainMenuQuitSelectLeftDemo = 28;
+static const CoordType kMainMenuQuitSelectTopDemo = 408;
+
+static const CoordType kOverviewLeft = 200;
+static const CoordType kOverviewTop = 208;
+
+static const CoordType kOverviewSelectLeft = 152;
+static const CoordType kOverviewSelectTop = 204;
+
+static const CoordType kStartLeft = 212;
+static const CoordType kStartTop = 256;
+
+static const CoordType kStartSelectLeft = 152;
+static const CoordType kStartSelectTop = 252;
+
+static const CoordType kRestoreLeft = 212;
+static const CoordType kRestoreTop = 296;
+
+static const CoordType kRestoreSelectLeft = 152;
+static const CoordType kRestoreSelectTop = 292;
+
+static const CoordType kDifficultyLeft = 320;
+static const CoordType kDifficultyTop = 340;
+
+static const CoordType kDifficultySelectLeft = 152;
+static const CoordType kDifficultySelectTop = 336;
+
+static const CoordType kCreditsLeft = 212;
+static const CoordType kCreditsTop = 388;
+
+static const CoordType kCreditsSelectLeft = 152;
+static const CoordType kCreditsSelectTop = 384;
+
+static const CoordType kMainMenuQuitLeft = 212;
+static const CoordType kMainMenuQuitTop = 428;
+
+static const CoordType kMainMenuQuitSelectLeft = 152;
+static const CoordType kMainMenuQuitSelectTop = 424;
+
+// Never set the current input handler to the MainMenu.
+MainMenu::MainMenu() : GameMenu(kMainMenuID), _menuBackground(0), _overviewButton(0),
+ _restoreButton(0), _adventureButton(0), _walkthroughButton(0), _startButton(0),
+ _creditsButton(0), _quitButton(0), _largeSelect(0), _smallSelect(0) {
+
+ bool isDemo = ((PegasusEngine *)g_engine)->isDemo();
+
+ if (isDemo)
+ _menuBackground.initFromPICTFile("Images/Demo/DemoMenu.pict");
+ else
+ _menuBackground.initFromPICTFile("Images/Main Menu/MainMenu.mac");
+ _menuBackground.setDisplayOrder(0);
+ _menuBackground.startDisplaying();
+ _menuBackground.show();
+
+ if (!isDemo) {
+ _overviewButton.initFromPICTFile("Images/Main Menu/pbOvervi.pict");
+ _overviewButton.setDisplayOrder(1);
+ _overviewButton.moveElementTo(kOverviewLeft, kOverviewTop);
+ _overviewButton.startDisplaying();
+
+ _restoreButton.initFromPICTFile("Images/Main Menu/pbRestor.pict");
+ _restoreButton.setDisplayOrder(1);
+ _restoreButton.moveElementTo(kRestoreLeft, kRestoreTop);
+ _restoreButton.startDisplaying();
+
+ _adventureButton.initFromPICTFile("Images/Main Menu/BtnAdv.pict");
+ _adventureButton.setDisplayOrder(1);
+ _adventureButton.moveElementTo(kDifficultyLeft, kDifficultyTop);
+ _adventureButton.startDisplaying();
+
+ _walkthroughButton.initFromPICTFile("Images/Main Menu/BtnWlk.pict");
+ _walkthroughButton.setDisplayOrder(1);
+ _walkthroughButton.moveElementTo(kDifficultyLeft, kDifficultyTop);
+ _walkthroughButton.startDisplaying();
+ }
+
+ if (isDemo)
+ _startButton.initFromPICTFile("Images/Demo/Start.pict");
+ else
+ _startButton.initFromPICTFile("Images/Main Menu/pbStart.pict");
+ _startButton.setDisplayOrder(1);
+ _startButton.moveElementTo(isDemo ? kStartLeftDemo : kStartLeft, isDemo ? kStartTopDemo : kStartTop);
+ _startButton.startDisplaying();
+
+ if (isDemo)
+ _creditsButton.initFromPICTFile("Images/Demo/Credits.pict");
+ else
+ _creditsButton.initFromPICTFile("Images/Main Menu/pbCredit.pict");
+ _creditsButton.setDisplayOrder(1);
+ _creditsButton.moveElementTo(isDemo ? kCreditsLeftDemo : kCreditsLeft, isDemo ? kCreditsTopDemo : kCreditsTop);
+ _creditsButton.startDisplaying();
+
+ if (isDemo)
+ _quitButton.initFromPICTFile("Images/Demo/Quit.pict");
+ else
+ _quitButton.initFromPICTFile("Images/Main Menu/pbQuit.pict");
+ _quitButton.setDisplayOrder(1);
+ _quitButton.moveElementTo(isDemo ? kMainMenuQuitLeftDemo : kMainMenuQuitLeft, isDemo ? kMainMenuQuitTopDemo : kMainMenuQuitTop);
+ _quitButton.startDisplaying();
+
+ if (isDemo)
+ _largeSelect.initFromPICTFile("Images/Demo/SelectL.pict", true);
+ else
+ _largeSelect.initFromPICTFile("Images/Main Menu/SelectL.pict", true);
+ _largeSelect.setDisplayOrder(1);
+ _largeSelect.startDisplaying();
+
+ if (isDemo)
+ _smallSelect.initFromPICTFile("Images/Demo/SelectS.pict", true);
+ else
+ _smallSelect.initFromPICTFile("Images/Main Menu/SelectS.pict", true);
+ _smallSelect.setDisplayOrder(1);
+ _smallSelect.startDisplaying();
+
+ _menuSelection = isDemo ? kFirstSelectionDemo : kFirstSelection;
+
+ _adventureMode = true;
+
+ _menuLoop.attachFader(&_menuFader);
+ _menuLoop.initFromAIFFFile("Sounds/Main Menu.aiff");
+
+ updateDisplay();
+}
+
+MainMenu::~MainMenu() {
+ if (_menuLoop.isPlaying())
+ stopMainMenuLoop();
+}
+
+void MainMenu::startMainMenuLoop() {
+ FaderMoveSpec spec;
+
+ _menuLoop.loopSound();
+ spec.makeTwoKnotFaderSpec(30, 0, 0, 30, 255);
+ _menuFader.startFaderSync(spec);
+}
+
+void MainMenu::stopMainMenuLoop() {
+ FaderMoveSpec spec;
+
+ spec.makeTwoKnotFaderSpec(30, 0, 255, 30, 0);
+ _menuFader.startFaderSync(spec);
+ _menuLoop.stopSound();
+}
+
+void MainMenu::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+ bool isDemo = vm->isDemo();
+
+ if (input.upButtonDown()) {
+ if (_menuSelection > (isDemo ? kFirstSelectionDemo : kFirstSelection)) {
+ _menuSelection--;
+ updateDisplay();
+ }
+ } else if (input.downButtonDown()) {
+ if (_menuSelection < (isDemo ? kLastSelectionDemo : kLastSelection)) {
+ _menuSelection++;
+ updateDisplay();
+ }
+ } else if (!isDemo && (input.leftButtonDown() || input.rightButtonDown())) {
+ if (_menuSelection == kMainMenuDifficulty) {
+ _adventureMode = !_adventureMode;
+ updateDisplay();
+ }
+ } else if (JMPPPInput::isMenuButtonPressInput(input)) {
+ if (isDemo) {
+ switch (_menuSelection) {
+ case kMainMenuCreditsDemo:
+ _creditsButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _creditsButton.hide();
+ setLastCommand(kMenuCmdCredits);
+ break;
+ case kMainMenuStartDemo:
+ _startButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _startButton.hide();
+ setLastCommand(kMenuCmdStartAdventure);
+ break;
+ case kMainMenuQuitDemo:
+ _quitButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _quitButton.hide();
+ setLastCommand(kMenuCmdQuit);
+ break;
+ }
+ } else {
+ switch (_menuSelection) {
+ case kMainMenuOverview:
+ _overviewButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _overviewButton.hide();
+ setLastCommand(kMenuCmdOverview);
+ break;
+ case kMainMenuRestore:
+ _restoreButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _restoreButton.hide();
+ setLastCommand(kMenuCmdRestore);
+ break;
+ case kMainMenuCredits:
+ _creditsButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _creditsButton.hide();
+ setLastCommand(kMenuCmdCredits);
+ break;
+ case kMainMenuStart:
+ _startButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _startButton.hide();
+ if (_adventureMode)
+ setLastCommand(kMenuCmdStartAdventure);
+ else
+ setLastCommand(kMenuCmdStartWalkthrough);
+ break;
+ case kMainMenuDifficulty:
+ _adventureMode = !_adventureMode;
+ updateDisplay();
+ break;
+ case kMainMenuQuit:
+ _quitButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _quitButton.hide();
+ setLastCommand(kMenuCmdQuit);
+ break;
+ }
+ }
+ }
+
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+void MainMenu::updateDisplay() {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ if (vm->isDemo()) {
+ switch (_menuSelection) {
+ case kMainMenuStartDemo:
+ _smallSelect.moveElementTo(kStartSelectLeftDemo, kStartSelectTopDemo);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kMainMenuCreditsDemo:
+ _smallSelect.moveElementTo(kCreditsSelectLeftDemo, kCreditsSelectTopDemo);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kMainMenuQuitDemo:
+ _largeSelect.moveElementTo(kMainMenuQuitSelectLeftDemo, kMainMenuQuitSelectTopDemo);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ }
+ } else {
+ switch (_menuSelection) {
+ case kMainMenuOverview:
+ _largeSelect.moveElementTo(kOverviewSelectLeft, kOverviewSelectTop);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ case kMainMenuRestore:
+ _smallSelect.moveElementTo(kRestoreSelectLeft, kRestoreSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kMainMenuDifficulty:
+ if (_adventureMode) {
+ _adventureButton.show();
+ _walkthroughButton.hide();
+ } else {
+ _walkthroughButton.show();
+ _adventureButton.hide();
+ }
+
+ _largeSelect.moveElementTo(kDifficultySelectLeft, kDifficultySelectTop);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ case kMainMenuStart:
+ _smallSelect.moveElementTo(kStartSelectLeft, kStartSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kMainMenuCredits:
+ _smallSelect.moveElementTo(kCreditsSelectLeft, kCreditsSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kMainMenuQuit:
+ _smallSelect.moveElementTo(kMainMenuQuitSelectLeft, kMainMenuQuitSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ }
+
+ vm->resetIntroTimer();
+ }
+}
+
+enum {
+ kCreditsMenuCoreTeam,
+ kCreditsMenuSupportTeam,
+ kCreditsMenuOriginalTeam,
+ kCreditsMenuTalent,
+ kCreditsMenuOtherTitles,
+ kCreditsMenuMainMenu,
+
+ kCreditsFirstSelection = kCreditsMenuCoreTeam,
+ kCreditsLastSelection = kCreditsMenuMainMenu
+};
+
+static const CoordType kCreditsMovieLeft = 288;
+static const CoordType kCreditsMovieTop = 0;
+
+static const CoordType kCoreTeamSelectLeft = 40;
+static const CoordType kCoreTeamSelectTop = 223;
+
+static const CoordType kSupportTeamSelectLeft = 40;
+static const CoordType kSupportTeamSelectTop = 259;
+
+static const CoordType kOriginalTeamSelectLeft = 40;
+static const CoordType kOriginalTeamSelectTop = 295;
+
+static const CoordType kTalentSelectLeft = 40;
+static const CoordType kTalentSelectTop = 331;
+
+static const CoordType kOtherTitlesSelectLeft = 40;
+static const CoordType kOtherTitlesSelectTop = 367;
+
+static const CoordType kCreditsMainMenuLeft = 32;
+static const CoordType kCreditsMainMenuTop = 412;
+
+static const CoordType kCreditsMainMenuSelectLeft = 30;
+static const CoordType kCreditsMainMenuSelectTop = 408;
+
+static const TimeValue kCoreTeamTime = 0;
+static const TimeValue kSupportTeamTime = 1920;
+static const TimeValue kOriginalTeamTime = 3000;
+static const TimeValue kTalentTime = 4440;
+static const TimeValue kOtherTitlesTime = 4680;
+
+static const TimeValue kFrameIncrement = 120; // Three frames...
+
+// Never set the current input handler to the CreditsMenu.
+CreditsMenu::CreditsMenu() : GameMenu(kCreditsMenuID), _menuBackground(0), _creditsMovie(0),
+ _mainMenuButton(0), _largeSelect(0), _smallSelect(0) {
+
+ _menuBackground.initFromPICTFile("Images/Credits/CredScrn.pict");
+ _menuBackground.setDisplayOrder(0);
+ _menuBackground.startDisplaying();
+ _menuBackground.show();
+
+ _creditsMovie.initFromMovieFile("Images/Credits/Credits.movie");
+ _creditsMovie.setDisplayOrder(1);
+ _creditsMovie.moveElementTo(kCreditsMovieLeft, kCreditsMovieTop);
+ _creditsMovie.startDisplaying();
+ _creditsMovie.show();
+ _creditsMovie.redrawMovieWorld();
+
+ _mainMenuButton.initFromPICTFile("Images/Credits/MainMenu.pict");
+ _mainMenuButton.setDisplayOrder(1);
+ _mainMenuButton.moveElementTo(kCreditsMainMenuLeft, kCreditsMainMenuTop);
+ _mainMenuButton.startDisplaying();
+
+ _largeSelect.initFromPICTFile("Images/Credits/SelectL.pict", true);
+ _largeSelect.setDisplayOrder(2);
+ _largeSelect.moveElementTo(kCreditsMainMenuSelectLeft, kCreditsMainMenuSelectTop);
+ _largeSelect.startDisplaying();
+
+ _smallSelect.initFromPICTFile("Images/Credits/SelectS.pict", true);
+ _smallSelect.setDisplayOrder(2);
+ _smallSelect.show();
+ _smallSelect.startDisplaying();
+
+ _menuSelection = -1;
+
+ newMenuSelection(kCreditsMenuCoreTeam);
+}
+
+// Assumes the new selection is never more than one away from the old...
+void CreditsMenu::newMenuSelection(const int newSelection) {
+ if (newSelection != _menuSelection) {
+ switch (newSelection) {
+ case kCreditsMenuCoreTeam:
+ _smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop);
+ _creditsMovie.setTime(kCoreTeamTime);
+ _creditsMovie.redrawMovieWorld();
+ break;
+ case kCreditsMenuSupportTeam:
+ _smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop);
+ _creditsMovie.setTime(kSupportTeamTime);
+ _creditsMovie.redrawMovieWorld();
+ break;
+ case kCreditsMenuOriginalTeam:
+ _smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop);
+ _creditsMovie.setTime(kOriginalTeamTime);
+ _creditsMovie.redrawMovieWorld();
+ break;
+ case kCreditsMenuTalent:
+ _smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop);
+ _creditsMovie.setTime(kTalentTime);
+ _creditsMovie.redrawMovieWorld();
+ break;
+ case kCreditsMenuOtherTitles:
+ _smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ _creditsMovie.setTime(kOtherTitlesTime);
+ _creditsMovie.redrawMovieWorld();
+ break;
+ case kCreditsMenuMainMenu:
+ _smallSelect.hide();
+ _largeSelect.show();
+ break;
+ }
+
+ _menuSelection = newSelection;
+ }
+}
+
+void CreditsMenu::newMovieTime(const TimeValue newTime) {
+ if (newTime < kSupportTeamTime) {
+ _smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop);
+ _menuSelection = kCreditsMenuCoreTeam;
+ } else if (newTime < kOriginalTeamTime) {
+ _smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop);
+ _menuSelection = kCreditsMenuSupportTeam;
+ } else if (newTime < kTalentTime) {
+ _smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop);
+ _menuSelection = kCreditsMenuOriginalTeam;
+ } else if (newTime < kOtherTitlesTime) {
+ _smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ _menuSelection = kCreditsMenuTalent;
+ } else if ((int)newTime == -120) {
+ // HACK: Avoid getting sent to the bottom button in the default case
+ return;
+ } else {
+ _smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ _menuSelection = kCreditsMenuOtherTitles;
+ }
+
+ _creditsMovie.setTime(newTime);
+ _creditsMovie.redrawMovieWorld();
+}
+
+void CreditsMenu::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (input.upButtonDown()) {
+ if (_menuSelection > kCreditsFirstSelection)
+ newMenuSelection(_menuSelection - 1);
+ } else if (input.downButtonDown()) {
+ if (_menuSelection < kCreditsLastSelection)
+ newMenuSelection(_menuSelection + 1);
+ } else if (input.leftButtonDown()) {
+ newMovieTime(_creditsMovie.getTime() - kFrameIncrement);
+ } else if (input.rightButtonDown()) {
+ newMovieTime(_creditsMovie.getTime() + kFrameIncrement);
+ } else if (JMPPPInput::isMenuButtonPressInput(input)) {
+ if (_menuSelection == kCreditsMenuMainMenu) {
+ _mainMenuButton.show();
+ ((PegasusEngine *)g_engine)->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _mainMenuButton.hide();
+ setLastCommand(kMenuCmdCreditsMainMenu);
+ }
+ }
+
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+static const CoordType kContinueLeft = 44;
+static const CoordType kContinueTop = 336;
+
+static const CoordType kContinueSelectLeft = 40;
+static const CoordType kContinueSelectTopDemo = 331;
+static const CoordType kContinueSelectTop = 332;
+
+static const CoordType kMainMenuLeftDemo = 44;
+static const CoordType kMainMenuTopDemo = 372;
+
+static const CoordType kMainMenuSelectLeftDemo = 40;
+static const CoordType kMainMenuSelectTopDemo = 367;
+
+static const CoordType kQuitLeftDemo = 32;
+static const CoordType kQuitTopDemo = 412;
+
+static const CoordType kQuitSelectLeftDemo = 28;
+static const CoordType kQuitSelectTopDemo = 408;
+
+static const CoordType kRestoreLeftDeath = 44;
+static const CoordType kRestoreTopDeath = 372;
+
+static const CoordType kRestoreSelectLeftDeath = 40;
+static const CoordType kRestoreSelectTopDeath = 368;
+
+static const CoordType kMainMenuLeft = 32;
+static const CoordType kMainMenuTop = 412;
+
+static const CoordType kMainMenuSelectLeft = 28;
+static const CoordType kMainMenuSelectTop = 408;
+
+enum {
+ kDeathScreenContinueDemo = 0,
+ kDeathScreenMainMenuDemo,
+ kDeathScreenQuitDemo,
+
+ kFirstDeathSelectionDemo = kDeathScreenContinueDemo,
+ kLastDeathSelectionDemo = kDeathScreenQuitDemo,
+
+ kDeathScreenContinue = 0,
+ kDeathScreenRestore,
+ kDeathScreenMainMenu,
+
+ kFirstDeathSelection = kDeathScreenContinue,
+ kLastDeathSelection = kDeathScreenMainMenu
+};
+
+// Never set the current input handler to the DeathMenu.
+DeathMenu::DeathMenu(const DeathReason deathReason) : GameMenu(kDeathMenuID), _deathBackground(0), _continueButton(0),
+ _mainMenuButton(0), _quitButton(0), _restoreButton(0), _largeSelect(0), _smallSelect(0) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+ bool isDemo = vm->isDemo();
+
+ _playerWon = (deathReason == kPlayerWonGame);
+
+ Common::String prefix = "Images/";
+ Common::String imageName;
+ if (isDemo) {
+ prefix += "Demo/";
+ imageName = prefix;
+
+ switch (deathReason) {
+ case kDeathFallOffCliff:
+ imageName += "dPfall";
+ break;
+ case kDeathEatenByDinosaur:
+ imageName += "dPdino";
+ break;
+ case kDeathStranded:
+ imageName += "dPstuck";
+ break;
+ default:
+ imageName += "dPdemowin";
+ break;
+ }
+
+ imageName += ".pict";
+ } else {
+ prefix += "Death Screens/";
+ imageName = prefix;
+
+ static const char *fileNames[] = {
+ "dAunmade", "dAbombed", "dAshot", "dAassass", "dAnuked",
+ "dTunmade", "dTshot", "dPfall", "dPdino", "dPstuck",
+ "dNchoke", "dNcaught", "dNcaught", "dNsub", "dNrobot1",
+ "dNrobot2", "dMfall", "dMcaught", "dMtracks", "dMrobot",
+ "dMtoast", "dMexplo1", "dMexplo2", "dMchoke1", "dMchoke2",
+ "dMdroid", "dMfall", "dMgears", "dMshutt1", "dMshutt2",
+ "dWpoison", "dWcaught", "dWplasma", "dWshot", "dAfinale"
+ };
+
+ imageName += fileNames[deathReason - 1];
+ imageName += ".pict";
+ }
+
+ _deathBackground.initFromPICTFile(imageName);
+ _deathReason = deathReason;
+
+ if (!isDemo) {
+ vm->_gfx->setCurSurface(_deathBackground.getSurface());
+ drawAllScores();
+ vm->_gfx->setCurSurface(vm->_gfx->getWorkArea());
+ }
+
+ _deathBackground.setDisplayOrder(0);
+ _deathBackground.startDisplaying();
+ _deathBackground.show();
+
+ if (isDemo) {
+ if (_playerWon) // Make credits button...
+ _continueButton.initFromPICTFile(prefix + "Credits.pict");
+ else // Make continue button...
+ _continueButton.initFromPICTFile(prefix + "Continue.pict");
+
+ _mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict");
+ _mainMenuButton.setDisplayOrder(1);
+ _mainMenuButton.moveElementTo(kMainMenuLeftDemo, kMainMenuTopDemo);
+ _mainMenuButton.startDisplaying();
+
+ _quitButton.initFromPICTFile(prefix + "Quit.pict");
+ _quitButton.setDisplayOrder(1);
+ _quitButton.moveElementTo(kQuitLeftDemo, kQuitTopDemo);
+ _quitButton.startDisplaying();
+
+ _menuSelection = kDeathScreenContinueDemo;
+ } else {
+ if (!_playerWon) {
+ _mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict");
+ _mainMenuButton.setDisplayOrder(1);
+ _mainMenuButton.moveElementTo(kMainMenuLeft, kMainMenuTop);
+ _mainMenuButton.startDisplaying();
+
+ _restoreButton.initFromPICTFile(prefix + "Restore.pict");
+ _restoreButton.setDisplayOrder(1);
+ _restoreButton.moveElementTo(kRestoreLeftDeath, kRestoreTopDeath);
+ _restoreButton.startDisplaying();
+ }
+
+ _continueButton.initFromPICTFile(prefix + "Continue.pict");
+
+ _menuSelection = kDeathScreenContinue;
+ }
+
+ _smallSelect.initFromPICTFile(prefix + "SelectS.pict", true);
+ _smallSelect.setDisplayOrder(2);
+ _smallSelect.startDisplaying();
+
+ _continueButton.setDisplayOrder(1);
+ _continueButton.moveElementTo(kContinueLeft, kContinueTop);
+ _continueButton.startDisplaying();
+
+ if (isDemo || !_playerWon) {
+ _largeSelect.initFromPICTFile(prefix + "SelectL.pict", true);
+ _largeSelect.setDisplayOrder(2);
+ _largeSelect.startDisplaying();
+ } else {
+ _triumphSound.initFromQuickTime("Sounds/Caldoria/Galactic Triumph");
+ _triumphSound.playSound();
+ }
+
+ updateDisplay();
+}
+
+void DeathMenu::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ if (input.upButtonDown()) {
+ if (_menuSelection > (vm->isDemo() ? kFirstDeathSelectionDemo : kFirstDeathSelection)) {
+ _menuSelection--;
+ updateDisplay();
+ }
+ } else if (input.downButtonDown() && (vm->isDemo() || !_playerWon)) {
+ if (_menuSelection < (vm->isDemo() ? kLastDeathSelectionDemo : kLastDeathSelection)) {
+ _menuSelection++;
+ updateDisplay();
+ }
+ } else if (JMPPPInput::isMenuButtonPressInput(input)) {
+ if (vm->isDemo()) {
+ switch (_menuSelection) {
+ case kDeathScreenContinueDemo:
+ _continueButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _continueButton.hide();
+ setLastCommand(kMenuCmdDeathContinue);
+ break;
+ case kDeathScreenQuitDemo:
+ _quitButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _quitButton.hide();
+ setLastCommand(kMenuCmdDeathQuitDemo);
+ break;
+ case kDeathScreenMainMenuDemo:
+ _mainMenuButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _mainMenuButton.hide();
+ setLastCommand(kMenuCmdDeathMainMenuDemo);
+ break;
+ }
+ } else {
+ switch (_menuSelection) {
+ case kDeathScreenContinue:
+ _continueButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _continueButton.hide();
+ setLastCommand(kMenuCmdDeathContinue);
+ break;
+ case kDeathScreenRestore:
+ _restoreButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _restoreButton.hide();
+ setLastCommand(kMenuCmdDeathRestore);
+ break;
+ case kDeathScreenMainMenu:
+ _mainMenuButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _mainMenuButton.hide();
+ setLastCommand(kMenuCmdDeathMainMenu);
+ break;
+ }
+ }
+ }
+
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+void DeathMenu::updateDisplay() {
+ if (((PegasusEngine *)g_engine)->isDemo()) {
+ switch (_menuSelection) {
+ case kDeathScreenContinueDemo:
+ _smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTopDemo);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kDeathScreenQuitDemo:
+ _largeSelect.moveElementTo(kQuitSelectLeftDemo, kQuitSelectTopDemo);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ case kDeathScreenMainMenuDemo:
+ _smallSelect.moveElementTo(kMainMenuSelectLeftDemo, kMainMenuSelectTopDemo);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ }
+ } else {
+ switch (_menuSelection) {
+ case kDeathScreenContinue:
+ _smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kDeathScreenRestore:
+ _smallSelect.moveElementTo(kRestoreSelectLeftDeath, kRestoreSelectTopDeath);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kDeathScreenMainMenu:
+ _largeSelect.moveElementTo(kMainMenuSelectLeft, kMainMenuSelectTop);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ }
+ }
+}
+
+void DeathMenu::drawAllScores() {
+ Surface numbers;
+ numbers.getImageFromPICTFile("Images/Death Screens/Numbers.pict");
+
+ Common::Rect scoreBounds;
+ GameScoreType caldoriaTotal = 0;
+
+ switch (_deathReason) {
+ case kDeathCardBomb:
+ case kDeathShotBySinclair:
+ case kDeathSinclairShotDelegate:
+ case kDeathNuclearExplosion:
+ case kDeathGassedInNorad:
+ case kDeathWokeUpNorad:
+ case kDeathArrestedInNorad:
+ case kDeathSubDestroyed:
+ case kDeathRobotThroughNoradDoor:
+ case kDeathRobotSubControlRoom:
+ case kDeathWrongShuttleLock:
+ case kDeathArrestedInMars:
+ case kDeathRunOverByPod:
+ case kDeathDidntGetOutOfWay:
+ case kDeathReactorBurn:
+ case kDeathDidntFindMarsBomb:
+ case kDeathDidntDisarmMarsBomb:
+ case kDeathNoMaskInMaze:
+ case kDeathNoAirInMaze:
+ case kDeathGroundByMazebot:
+ case kDeathMissedOreBucket:
+ case kDeathDidntLeaveBucket:
+ case kDeathRanIntoCanyonWall:
+ case kDeathRanIntoSpaceJunk:
+ case kDeathDidntStopPoison:
+ case kDeathArrestedInWSC:
+ case kDeathHitByPlasma:
+ case kDeathShotOnCatwalk:
+ case kPlayerWonGame:
+ caldoriaTotal += kMaxCaldoriaTSAScoreAfter;
+ scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5,
+ kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5 + kDeathScreenScoreHeight);
+ drawScore(GameState.getGandhiScore(), kMaxGandhiScore, scoreBounds, &numbers);
+
+ scoreBounds.translate(0, kDeathScreenScoreSkipVert);
+ drawScore(GameState.getWSCScore(), kMaxWSCScore, scoreBounds, &numbers);
+
+ scoreBounds.translate(0, kDeathScreenScoreSkipVert);
+ drawScore(GameState.getNoradScore(), kMaxNoradScore, scoreBounds, &numbers);
+
+ scoreBounds.translate(0, kDeathScreenScoreSkipVert);
+ drawScore(GameState.getMarsScore(), kMaxMarsScore, scoreBounds, &numbers);
+ // fall through
+ case kDeathFallOffCliff:
+ case kDeathEatenByDinosaur:
+ case kDeathStranded:
+ case kDeathShotByTSARobots:
+ scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert,
+ kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert + kDeathScreenScoreHeight);
+ drawScore(GameState.getPrehistoricScore(), kMaxPrehistoricScore, scoreBounds, &numbers);
+ // fall through
+ case kDeathUncreatedInCaldoria:
+ case kDeathUncreatedInTSA:
+ scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop, kDeathScreenScoreLeft + kDeathScreenScoreWidth,
+ kDeathScreenScoreTop + kDeathScreenScoreHeight);
+ caldoriaTotal += kMaxCaldoriaTSAScoreBefore;
+ drawScore(GameState.getCaldoriaTSAScore(), caldoriaTotal, scoreBounds, &numbers);
+
+ scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6,
+ kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6 + kDeathScreenScoreHeight);
+
+ drawScore(GameState.getTotalScore(), kMaxTotalScore, scoreBounds, &numbers);
+ break;
+ }
+}
+
+enum {
+ kPauseMenuSave,
+ kPauseMenuContinue,
+ kPauseMenuRestore,
+ kPauseMenuSoundFX,
+ kPauseMenuAmbience,
+ kPauseMenuWalkthru,
+ kPauseMenuQuitToMainMenu,
+
+ kFirstPauseSelection = kPauseMenuSave,
+ kLastPauseSelection = kPauseMenuQuitToMainMenu
+};
+
+static const CoordType kPauseLeft = 194;
+static const CoordType kPauseTop = 68;
+
+static const CoordType kSaveGameLeft = kPauseLeft + 6;
+static const CoordType kSaveGameTop = kPauseTop + 56;
+
+static const CoordType kSaveGameSelectLeft = kPauseLeft - 44;
+static const CoordType kSaveGameSelectTop = kPauseTop + 52;
+
+static const CoordType kPauseContinueLeft = kPauseLeft + 18;
+static const CoordType kPauseContinueTop = kPauseTop + 100;
+
+static const CoordType kPauseContinueSelectLeft = kPauseLeft - 44;
+static const CoordType kPauseContinueSelectTop = kPauseTop + 95;
+
+static const CoordType kPauseRestoreLeft = kPauseLeft + 18;
+static const CoordType kPauseRestoreTop = kPauseTop + 136;
+
+static const CoordType kPauseRestoreSelectLeft = kPauseLeft - 44;
+static const CoordType kPauseRestoreSelectTop = kPauseTop + 131;
+
+static const CoordType kSoundFXLeft = kPauseLeft + 128;
+static const CoordType kSoundFXTop = kPauseTop + 187;
+static const CoordType kSoundFXRight = kSoundFXLeft + 96;
+static const CoordType kSoundFXBottom = kSoundFXTop + 14;
+
+static const CoordType kSoundFXSelectLeft = kPauseLeft - 44;
+static const CoordType kSoundFXSelectTop = kPauseTop + 172;
+
+static const CoordType kAmbienceLeft = kPauseLeft + 128;
+static const CoordType kAmbienceTop = kPauseTop + 227;
+static const CoordType kAmbienceRight = kAmbienceLeft + 96;
+static const CoordType kAmbienceBottom = kAmbienceTop + 14;
+
+static const CoordType kAmbienceSelectLeft = kPauseLeft - 44;
+static const CoordType kAmbienceSelectTop = kPauseTop + 212;
+
+static const CoordType kWalkthruLeft = kPauseLeft + 128;
+static const CoordType kWalkthruTop = kPauseTop + 264;
+
+static const CoordType kWalkthruSelectLeft = kPauseLeft - 44;
+static const CoordType kWalkthruSelectTop = kPauseTop + 255;
+
+static const CoordType kQuitLeft = kPauseLeft + 18;
+static const CoordType kQuitTop = kPauseTop + 302;
+
+static const CoordType kQuitSelectLeft = kPauseLeft - 44;
+static const CoordType kQuitSelectTop = kPauseTop + 297;
+
+// These are relative to the pause background.
+static const CoordType kPauseScoreLeft = 130;
+static const CoordType kPauseScoreTop = 34;
+static const CoordType kPauseScoreRight = kPauseScoreLeft + 108;
+static const CoordType kPauseScoreBottom = kPauseScoreTop + 12;
+
+// Never set the current input handler to the CPauseMenu.
+PauseMenu::PauseMenu() : GameMenu(kPauseMenuID), _pauseBackground(0), _saveButton(0), _restoreButton(0),
+ _walkthroughButton(0), _continueButton(0), _soundFXLevel(0), _ambienceLevel(0), _quitButton(0),
+ _largeSelect(0), _smallSelect(0) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ _pauseBackground.initFromPICTFile("Images/Pause Screen/PausScrn.pict", true);
+
+ if (!vm->isDemo()) {
+ Surface numbers;
+ numbers.getImageFromPICTFile("Images/Pause Screen/Numbers.pict");
+ vm->_gfx->setCurSurface(_pauseBackground.getSurface());
+ drawScore(GameState.getTotalScore(), kMaxTotalScore,
+ Common::Rect(kPauseScoreLeft, kPauseScoreTop, kPauseScoreRight, kPauseScoreBottom), &numbers);
+ vm->_gfx->setCurSurface(vm->_gfx->getWorkArea());
+ }
+
+ _pauseBackground.setDisplayOrder(kPauseMenuOrder);
+ _pauseBackground.moveElementTo(kPauseLeft, kPauseTop);
+ _pauseBackground.startDisplaying();
+ _pauseBackground.show();
+
+ if (!vm->isDemo()) {
+ _saveButton.initFromPICTFile("Images/Pause Screen/SaveGame.pict");
+ _saveButton.setDisplayOrder(kSaveGameOrder);
+ _saveButton.moveElementTo(kSaveGameLeft, kSaveGameTop);
+ _saveButton.startDisplaying();
+
+ _restoreButton.initFromPICTFile("Images/Pause Screen/Restore.pict");
+ _restoreButton.setDisplayOrder(kRestoreOrder);
+ _restoreButton.moveElementTo(kPauseRestoreLeft, kPauseRestoreTop);
+ _restoreButton.startDisplaying();
+
+ _walkthroughButton.initFromPICTFile("Images/Pause Screen/Walkthru.pict");
+ _walkthroughButton.setDisplayOrder(kWalkthruOrder);
+ _walkthroughButton.moveElementTo(kWalkthruLeft, kWalkthruTop);
+ _walkthroughButton.startDisplaying();
+
+ if (GameState.getWalkthroughMode())
+ _walkthroughButton.show();
+ }
+
+ _continueButton.initFromPICTFile("Images/Pause Screen/Continue.pict");
+ _continueButton.setDisplayOrder(kContinueOrder);
+ _continueButton.moveElementTo(kPauseContinueLeft, kPauseContinueTop);
+ _continueButton.startDisplaying();
+
+ _soundFXLevel.setDisplayOrder(kSoundFXOrder);
+ _soundFXLevel.setBounds(Common::Rect(kSoundFXLeft, kSoundFXTop, kSoundFXRight, kSoundFXBottom));
+ _soundFXLevel.startDisplaying();
+ _soundFXLevel.show();
+ _soundFXLevel.setSoundLevel(vm->getSoundFXLevel());
+
+ _ambienceLevel.setDisplayOrder(kAmbienceOrder);
+ _ambienceLevel.setBounds(Common::Rect(kAmbienceLeft, kAmbienceTop, kAmbienceRight, kAmbienceBottom));
+ _ambienceLevel.startDisplaying();
+ _ambienceLevel.show();
+ _ambienceLevel.setSoundLevel(vm->getAmbienceLevel());
+
+ _quitButton.initFromPICTFile("Images/Pause Screen/Quit2MM.pict");
+ _quitButton.setDisplayOrder(kQuitToMainMenuOrder);
+ _quitButton.moveElementTo(kQuitLeft, kQuitTop);
+ _quitButton.startDisplaying();
+
+ _largeSelect.initFromPICTFile("Images/Pause Screen/SelectL.pict", true);
+ _largeSelect.setDisplayOrder(kPauseLargeHiliteOrder);
+ _largeSelect.startDisplaying();
+
+ _smallSelect.initFromPICTFile("Images/Pause Screen/SelectS.pict", true);
+ _smallSelect.setDisplayOrder(kPauseSmallHiliteOrder);
+ _smallSelect.startDisplaying();
+
+ _menuSelection = (vm->isDemo()) ? kPauseMenuContinue : kPauseMenuSave;
+
+ updateDisplay();
+}
+
+void PauseMenu::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ if (input.upButtonDown()) {
+ if (vm->isDemo()) {
+ if (_menuSelection > kPauseMenuContinue) {
+ switch (_menuSelection) {
+ case kPauseMenuSoundFX:
+ _menuSelection = kPauseMenuContinue;
+ break;
+ case kPauseMenuAmbience:
+ _menuSelection = kPauseMenuSoundFX;
+ break;
+ case kPauseMenuQuitToMainMenu:
+ _menuSelection = kPauseMenuAmbience;
+ break;
+ }
+ updateDisplay();
+ }
+ } else {
+ if (_menuSelection > kFirstPauseSelection) {
+ _menuSelection--;
+ updateDisplay();
+ }
+ }
+ } else if (input.downButtonDown()) {
+ if (vm->isDemo()) {
+ if (_menuSelection < kPauseMenuQuitToMainMenu) {
+ switch (_menuSelection) {
+ case kPauseMenuContinue:
+ _menuSelection = kPauseMenuSoundFX;
+ break;
+ case kPauseMenuSoundFX:
+ _menuSelection = kPauseMenuAmbience;
+ break;
+ case kPauseMenuAmbience:
+ _menuSelection = kPauseMenuQuitToMainMenu;
+ break;
+ }
+ updateDisplay();
+ }
+ } else {
+ if (_menuSelection < kLastPauseSelection) {
+ _menuSelection++;
+ updateDisplay();
+ }
+ }
+ } else if (input.leftButtonDown()) {
+ if (_menuSelection == kPauseMenuSoundFX) {
+ _soundFXLevel.decrementLevel();
+ vm->setSoundFXLevel(_soundFXLevel.getSoundLevel());
+ } else if (_menuSelection == kPauseMenuAmbience) {
+ _ambienceLevel.decrementLevel();
+ vm->setAmbienceLevel(_ambienceLevel.getSoundLevel());
+ } else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) {
+ GameState.setWalkthroughMode(!GameState.getWalkthroughMode());
+ if (GameState.getWalkthroughMode())
+ _walkthroughButton.show();
+ else
+ _walkthroughButton.hide();
+ }
+ } else if (input.rightButtonDown()) {
+ if (_menuSelection == kPauseMenuSoundFX) {
+ _soundFXLevel.incrementLevel();
+ vm->setSoundFXLevel(_soundFXLevel.getSoundLevel());
+ } else if (_menuSelection == kPauseMenuAmbience) {
+ _ambienceLevel.incrementLevel();
+ vm->setAmbienceLevel(_ambienceLevel.getSoundLevel());
+ } else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) {
+ GameState.setWalkthroughMode(!GameState.getWalkthroughMode());
+ if (GameState.getWalkthroughMode())
+ _walkthroughButton.show();
+ else
+ _walkthroughButton.hide();
+ }
+ } else if (JMPPPInput::isMenuButtonPressInput(input)) {
+ switch (_menuSelection) {
+ case kPauseMenuSave:
+ _saveButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _saveButton.hide();
+ setLastCommand(kMenuCmdPauseSave);
+ break;
+ case kPauseMenuRestore:
+ _restoreButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _restoreButton.hide();
+ setLastCommand(kMenuCmdPauseRestore);
+ break;
+ case kPauseMenuContinue:
+ _continueButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _continueButton.hide();
+ setLastCommand(kMenuCmdPauseContinue);
+ break;
+ case kPauseMenuWalkthru:
+ GameState.setWalkthroughMode(!GameState.getWalkthroughMode());
+ if (GameState.getWalkthroughMode())
+ _walkthroughButton.show();
+ else
+ _walkthroughButton.hide();
+ break;
+ case kPauseMenuQuitToMainMenu:
+ _quitButton.show();
+ vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale);
+ _quitButton.hide();
+ setLastCommand(kMenuCmdPauseQuit);
+ break;
+ }
+ }
+
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+void PauseMenu::updateDisplay() {
+ switch (_menuSelection) {
+ case kPauseMenuSave:
+ _largeSelect.moveElementTo(kSaveGameSelectLeft, kSaveGameSelectTop);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ case kPauseMenuContinue:
+ _smallSelect.moveElementTo(kPauseContinueSelectLeft, kPauseContinueSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kPauseMenuRestore:
+ _smallSelect.moveElementTo(kPauseRestoreSelectLeft, kPauseRestoreSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ case kPauseMenuSoundFX:
+ _largeSelect.moveElementTo(kSoundFXSelectLeft, kSoundFXSelectTop);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ case kPauseMenuAmbience:
+ _largeSelect.moveElementTo(kAmbienceSelectLeft, kAmbienceSelectTop);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ case kPauseMenuWalkthru:
+ _largeSelect.moveElementTo(kWalkthruSelectLeft, kWalkthruSelectTop);
+ _largeSelect.show();
+ _smallSelect.hide();
+ break;
+ case kPauseMenuQuitToMainMenu:
+ _smallSelect.moveElementTo(kQuitSelectLeft, kQuitSelectTop);
+ _smallSelect.show();
+ _largeSelect.hide();
+ break;
+ }
+
+ ((PegasusEngine *)g_engine)->resetIntroTimer();
+}
+
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/menu.h b/engines/pegasus/menu.h
new file mode 100644
index 0000000000..288b846093
--- /dev/null
+++ b/engines/pegasus/menu.h
@@ -0,0 +1,171 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_MENU_H
+#define PEGASUS_MENU_H
+
+#include "pegasus/constants.h"
+#include "pegasus/fader.h"
+#include "pegasus/input.h"
+#include "pegasus/movie.h"
+#include "pegasus/sound.h"
+#include "pegasus/surface.h"
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+class GameMenu : public IDObject, public InputHandler {
+public:
+ GameMenu(const uint32);
+ virtual ~GameMenu() {}
+
+ virtual void becomeCurrentHandler();
+ virtual void restorePreviousHandler();
+
+ GameMenuCommand getLastCommand() { return _lastCommand; }
+ void clearLastCommand() { _lastCommand = kMenuCmdNoCommand; }
+
+protected:
+ void setLastCommand(const GameMenuCommand command) { _lastCommand = command; }
+
+ InputHandler *_previousHandler;
+ GameMenuCommand _lastCommand;
+
+ void drawScore(GameScoreType, GameScoreType, const Common::Rect &, Surface *);
+
+private:
+ void drawNumber(GameScoreType, CoordType &, CoordType, Surface *);
+};
+
+class Hotspot;
+
+class MainMenu : public GameMenu {
+public:
+ MainMenu();
+ virtual ~MainMenu();
+
+ virtual void handleInput(const Input &input, const Hotspot *);
+ void startMainMenuLoop();
+ void stopMainMenuLoop();
+
+protected:
+ void updateDisplay();
+
+ uint32 _menuSelection;
+
+ // Full and Demo
+ Picture _menuBackground;
+ Picture _startButton;
+ Picture _creditsButton;
+ Picture _quitButton;
+ Picture _largeSelect;
+ Picture _smallSelect;
+
+ // Full only
+ bool _adventureMode;
+ Picture _overviewButton;
+ Picture _restoreButton;
+ Picture _adventureButton;
+ Picture _walkthroughButton;
+
+ Sound _menuLoop;
+ SoundFader _menuFader;
+};
+
+class CreditsMenu : public GameMenu {
+public:
+ CreditsMenu();
+ virtual ~CreditsMenu() {}
+
+ virtual void handleInput(const Input &input, const Hotspot *);
+
+protected:
+ void newMenuSelection(const int);
+ void newMovieTime(const TimeValue);
+
+ int _menuSelection;
+ Picture _menuBackground;
+ Movie _creditsMovie;
+ Picture _mainMenuButton;
+ Picture _largeSelect;
+ Picture _smallSelect;
+};
+
+class DeathMenu : public GameMenu {
+public:
+ DeathMenu(const DeathReason);
+ virtual ~DeathMenu() {}
+
+ virtual void handleInput(const Input &input, const Hotspot *);
+
+ bool playerWon() { return _playerWon; }
+
+protected:
+ void drawAllScores();
+
+ void updateDisplay();
+
+ bool _playerWon;
+ int _menuSelection;
+ DeathReason _deathReason;
+
+ Picture _deathBackground;
+ Picture _continueButton;
+ Picture _restoreButton;
+ Picture _mainMenuButton;
+ Picture _quitButton;
+
+ Picture _largeSelect;
+ Picture _smallSelect;
+
+ Sound _triumphSound;
+};
+
+class PauseMenu : public GameMenu {
+public:
+ PauseMenu();
+ virtual ~PauseMenu() {}
+
+ virtual void handleInput(const Input &input, const Hotspot *);
+
+protected:
+ void updateDisplay();
+
+ uint32 _menuSelection;
+ Picture _pauseBackground;
+ Picture _saveButton;
+ Picture _restoreButton;
+ Picture _walkthroughButton;
+ Picture _continueButton;
+ SoundLevel _soundFXLevel;
+ SoundLevel _ambienceLevel;
+ Picture _quitButton;
+ Picture _largeSelect;
+ Picture _smallSelect;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk
new file mode 100644
index 0000000000..cb44a04171
--- /dev/null
+++ b/engines/pegasus/module.mk
@@ -0,0 +1,100 @@
+MODULE := engines/pegasus
+
+MODULE_OBJS = \
+ compass.o \
+ console.o \
+ cursor.o \
+ detection.o \
+ elements.o \
+ energymonitor.o \
+ fader.o \
+ gamestate.o \
+ graphics.o \
+ hotspot.o \
+ input.o \
+ interface.o \
+ menu.o \
+ movie.o \
+ notification.o \
+ pegasus.o \
+ sound.o \
+ surface.o \
+ timers.o \
+ transition.o \
+ util.o \
+ ai/ai_action.o \
+ ai/ai_area.o \
+ ai/ai_condition.o \
+ ai/ai_rule.o \
+ items/autodragger.o \
+ items/inventory.o \
+ items/inventorypicture.o \
+ items/item.o \
+ items/itemdragger.o \
+ items/itemlist.o \
+ items/biochips/aichip.o \
+ items/biochips/biochipitem.o \
+ items/biochips/mapchip.o \
+ items/biochips/mapimage.o \
+ items/biochips/opticalchip.o \
+ items/biochips/pegasuschip.o \
+ items/biochips/retscanchip.o \
+ items/biochips/shieldchip.o \
+ items/inventory/airmask.o \
+ items/inventory/gascanister.o \
+ items/inventory/inventoryitem.o \
+ items/inventory/keycard.o \
+ neighborhood/door.o \
+ neighborhood/exit.o \
+ neighborhood/extra.o \
+ neighborhood/hotspotinfo.o \
+ neighborhood/neighborhood.o \
+ neighborhood/spot.o \
+ neighborhood/turn.o \
+ neighborhood/view.o \
+ neighborhood/zoom.o \
+ neighborhood/caldoria/caldoria.o \
+ neighborhood/caldoria/caldoria4dsystem.o \
+ neighborhood/caldoria/caldoriabomb.o \
+ neighborhood/caldoria/caldoriamessages.o \
+ neighborhood/caldoria/caldoriamirror.o \
+ neighborhood/mars/energybeam.o \
+ neighborhood/mars/gravitoncannon.o \
+ neighborhood/mars/hermite.o \
+ neighborhood/mars/mars.o \
+ neighborhood/mars/planetmover.o \
+ neighborhood/mars/reactor.o \
+ neighborhood/mars/robotship.o \
+ neighborhood/mars/shuttleenergymeter.o \
+ neighborhood/mars/shuttlehud.o \
+ neighborhood/mars/shuttleweapon.o \
+ neighborhood/mars/spacechase3d.o \
+ neighborhood/mars/spacejunk.o \
+ neighborhood/mars/tractorbeam.o \
+ neighborhood/norad/norad.o \
+ neighborhood/norad/noradelevator.o \
+ neighborhood/norad/pressuredoor.o \
+ neighborhood/norad/pressuretracker.o \
+ neighborhood/norad/subcontrolroom.o \
+ neighborhood/norad/subplatform.o \
+ neighborhood/norad/alpha/ecrmonitor.o \
+ neighborhood/norad/alpha/fillingstation.o \
+ neighborhood/norad/alpha/noradalpha.o \
+ neighborhood/norad/alpha/panorama.o \
+ neighborhood/norad/alpha/panoramascroll.o \
+ neighborhood/norad/delta/globegame.o \
+ neighborhood/norad/delta/noraddelta.o \
+ neighborhood/prehistoric/prehistoric.o \
+ neighborhood/tsa/fulltsa.o \
+ neighborhood/tsa/tinytsa.o \
+ neighborhood/wsc/moleculebin.o \
+ neighborhood/wsc/wsc.o
+
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_PEGASUS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/pegasus/movie.cpp b/engines/pegasus/movie.cpp
new file mode 100644
index 0000000000..75c287c7a6
--- /dev/null
+++ b/engines/pegasus/movie.cpp
@@ -0,0 +1,280 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "graphics/surface.h"
+#include "video/qt_decoder.h"
+#include "video/video_decoder.h"
+
+#include "pegasus/movie.h"
+
+namespace Pegasus {
+
+Movie::Movie(const DisplayElementID id) : Animation(id) {
+ _video = 0;
+ setScale(600);
+}
+
+Movie::~Movie() {
+ releaseMovie();
+}
+
+// *** Make sure this will stop displaying the movie.
+
+void Movie::releaseMovie() {
+ if (_video) {
+ delete _video;
+ _video = 0;
+ disposeAllCallBacks();
+ deallocateSurface();
+ }
+
+ setBounds(Common::Rect(0, 0, 0, 0));
+}
+
+void Movie::initFromMovieFile(const Common::String &fileName, bool transparent) {
+ _transparent = transparent;
+
+ releaseMovie();
+ _video = new Video::QuickTimeDecoder();
+ if (!_video->loadFile(fileName)) {
+ // Replace any colon with an underscore, since only Mac OS X
+ // supports that. See PegasusEngine::detectOpeningClosingDirectory()
+ // for more info.
+ Common::String newName(fileName);
+ if (newName.contains(':'))
+ for (uint i = 0; i < newName.size(); i++)
+ if (newName[i] == ':')
+ newName.setChar('_', i);
+
+ if (!_video->loadFile(newName))
+ error("Could not load video '%s'", fileName.c_str());
+ }
+
+ Common::Rect bounds(0, 0, _video->getWidth(), _video->getHeight());
+ sizeElement(_video->getWidth(), _video->getHeight());
+ _movieBox = bounds;
+
+ if (!isSurfaceValid())
+ allocateSurface(bounds);
+
+ setStart(0, getScale());
+ TimeBase::setStop(_video->getDuration().convertToFramerate(getScale()).totalNumberOfFrames(), getScale());
+}
+
+void Movie::redrawMovieWorld() {
+ if (_video && _video->needsUpdate()) {
+ const Graphics::Surface *frame = _video->decodeNextFrame();
+
+ if (!frame)
+ return;
+
+ // Make sure we have a surface in the current pixel format
+ Graphics::Surface *convertedFrame = 0;
+
+ if (frame->format != g_system->getScreenFormat()) {
+ convertedFrame = frame->convertTo(g_system->getScreenFormat());
+ frame = convertedFrame;
+ }
+
+ // Copy to the surface using _movieBox
+ uint16 width = MIN<int>(frame->w, _movieBox.width());
+ uint16 height = MIN<int>(frame->h, _movieBox.height());
+
+ for (uint16 y = 0; y < height; y++)
+ memcpy((byte *)_surface->getBasePtr(_movieBox.left, _movieBox.top + y), (const byte *)frame->getBasePtr(0, y), width * frame->format.bytesPerPixel);
+
+ if (convertedFrame) {
+ convertedFrame->free();
+ delete convertedFrame;
+ }
+
+ triggerRedraw();
+ }
+}
+
+void Movie::draw(const Common::Rect &r) {
+ Common::Rect worldBounds = _movieBox;
+ Common::Rect elementBounds;
+ getBounds(elementBounds);
+
+ worldBounds.moveTo(elementBounds.left, elementBounds.top);
+ Common::Rect r1 = r.findIntersectingRect(worldBounds);
+
+ Common::Rect r2 = r1;
+ r2.translate(_movieBox.left - elementBounds.left, _movieBox.top - elementBounds.top);
+ drawImage(r2, r1);
+}
+
+void Movie::moveMovieBoxTo(const CoordType h, const CoordType v) {
+ _movieBox.moveTo(h, v);
+}
+
+void Movie::setStop(const TimeValue stopTime, const TimeScale scale) {
+ TimeBase::setStop(stopTime, scale);
+
+ if (_video)
+ _video->setEndTime(Audio::Timestamp(0, _stopTime, _stopScale));
+}
+
+void Movie::setVolume(uint16 volume) {
+ if (_video)
+ _video->setVolume(MIN<uint>(volume, 0xFF));
+}
+
+void Movie::setTime(const TimeValue time, const TimeScale scale) {
+ if (_video) {
+ // Don't go past the ends of the movie
+ Common::Rational timeFrac = Common::Rational(time, ((scale == 0) ? getScale() : scale));
+
+ if (timeFrac < Common::Rational(_startTime, _startScale))
+ timeFrac = Common::Rational(_startTime, _startScale);
+ else if (timeFrac >= Common::Rational(_stopTime, _stopScale))
+ return;
+
+ _video->seek(Audio::Timestamp(0, timeFrac.getNumerator(), timeFrac.getDenominator()));
+ _time = timeFrac;
+ _lastMillis = 0;
+ }
+}
+
+void Movie::setRate(const Common::Rational rate) {
+ if (rate != 1 && rate != 0) {
+ warning("Cannot set movie rate");
+ start();
+ return;
+ }
+
+ TimeBase::setRate(rate);
+}
+
+void Movie::start() {
+ if (_video)
+ _video->start();
+
+ TimeBase::start();
+}
+
+void Movie::stop() {
+ if (_video)
+ _video->stop();
+
+ TimeBase::stop();
+}
+
+void Movie::resume() {
+ if (_paused) {
+ if (_video)
+ _video->pauseVideo(false);
+
+ _paused = false;
+ }
+}
+
+void Movie::pause() {
+ if (isRunning() && !_paused) {
+ if (_video)
+ _video->pauseVideo(true);
+
+ _paused = true;
+ _pauseStart = g_system->getMillis();
+ }
+}
+
+TimeValue Movie::getDuration(const TimeScale scale) const {
+ // Unlike TimeBase::getDuration(), this returns the whole duration of the movie
+ // The original source has a TODO to make this behave like TimeBase::getDuration(),
+ // but the problem is that too much code requires this function to behave this way...
+
+ if (_video)
+ return _video->getDuration().convertToFramerate(((scale == 0) ? getScale() : scale)).totalNumberOfFrames();
+
+ return 0;
+}
+
+void Movie::updateTime() {
+ // The reason why we overrode TimeBase's updateTime():
+ // Again, avoiding timers and handling it here
+ if (_video && _video->isPlaying() && !_video->isPaused()) {
+ redrawMovieWorld();
+
+ uint32 startTime = _startTime * getScale() / _startScale;
+ uint32 stopTime = _stopTime * getScale() / _stopScale;
+ uint32 actualTime = CLIP<int>(_video->getTime() * getScale() / 1000, startTime, stopTime);
+
+ // HACK: Due to the inaccuracy of the time, we won't actually allow us to hit
+ // the stop time unless we've actually reached the end of the segment.
+ if (actualTime == stopTime && !_video->endOfVideo())
+ actualTime--;
+
+ _time = Common::Rational(actualTime, getScale());
+ }
+}
+
+GlowingMovie::GlowingMovie(const DisplayElementID id) : Movie(id) {
+ _glowing = false;
+}
+
+void GlowingMovie::draw(const Common::Rect &r) {
+ // Make sure the rectangles are clipped properly, OR guarantee that _bounds will
+ // never fall off the screen.
+ if (_glowing) {
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ copyToCurrentPortTransparentGlow(_movieBox, bounds);
+ } else {
+ Movie::draw(r);
+ }
+}
+
+void GlowingMovie::setBounds(const Common::Rect &r) {
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ if (r != bounds) {
+ // Avoid Movie::setBounds.
+ // clone2727 asks why, but goes along with it
+ Animation::setBounds(r);
+ }
+}
+
+ScalingMovie::ScalingMovie(const DisplayElementID id) : GlowingMovie(id) {
+}
+
+void ScalingMovie::draw(const Common::Rect &) {
+ // Make sure the rectangles are clipped properly, OR guarantee that _bounds will
+ // never fall off the screen.
+
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ if (_glowing)
+ scaleTransparentCopyGlow(_movieBox, bounds);
+ else
+ scaleTransparentCopy(_movieBox, bounds);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/movie.h b/engines/pegasus/movie.h
new file mode 100644
index 0000000000..9b9cc2bdbe
--- /dev/null
+++ b/engines/pegasus/movie.h
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_MOVIE_H
+#define PEGASUS_MOVIE_H
+
+#include "common/str.h"
+
+#include "pegasus/elements.h"
+#include "pegasus/surface.h"
+
+namespace Video {
+class VideoDecoder;
+}
+
+namespace Pegasus {
+
+class Movie : public Animation, public PixelImage {
+public:
+ Movie(const DisplayElementID);
+ virtual ~Movie();
+
+ virtual void initFromMovieFile(const Common::String &fileName, bool transparent = false);
+
+ bool isMovieValid() { return _video != 0; }
+
+ virtual void releaseMovie();
+
+ virtual void draw(const Common::Rect &);
+ virtual void redrawMovieWorld();
+
+ virtual void setTime(const TimeValue, const TimeScale = 0);
+
+ virtual void setRate(const Common::Rational);
+
+ virtual void start();
+ virtual void stop();
+ virtual void resume();
+ virtual void pause();
+
+ virtual void moveMovieBoxTo(const CoordType, const CoordType);
+
+ virtual void setStop(const TimeValue, const TimeScale = 0);
+
+ virtual TimeValue getDuration(const TimeScale = 0) const;
+
+ // *** HACK ALERT
+ Video::VideoDecoder *getMovie() { return _video; }
+ void setVolume(uint16);
+
+protected:
+ void updateTime();
+
+ Video::VideoDecoder *_video;
+ Common::Rect _movieBox;
+};
+
+class GlowingMovie : public Movie {
+public:
+ GlowingMovie(DisplayElementID);
+ virtual ~GlowingMovie() {}
+
+ virtual void draw(const Common::Rect &);
+
+ void setBounds(const Common::Rect &);
+
+ void setGlowing(const bool glowing) { _glowing = glowing; }
+
+protected:
+ bool _glowing;
+};
+
+class ScalingMovie : public GlowingMovie {
+public:
+ ScalingMovie(DisplayElementID);
+ virtual ~ScalingMovie() {}
+
+ virtual void draw(const Common::Rect &);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.cpp b/engines/pegasus/neighborhood/caldoria/caldoria.cpp
new file mode 100644
index 0000000000..140e6e8093
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoria.cpp
@@ -0,0 +1,1962 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "video/qt_decoder.h"
+
+#include "pegasus/cursor.h"
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/interface.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/biochipitem.h"
+#include "pegasus/neighborhood/caldoria/caldoria.h"
+#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h"
+#include "pegasus/neighborhood/caldoria/caldoriabomb.h"
+#include "pegasus/neighborhood/caldoria/caldoriamessages.h"
+#include "pegasus/neighborhood/caldoria/caldoriamirror.h"
+#include "pegasus/neighborhood/tsa/fulltsa.h"
+
+namespace Pegasus {
+
+static const int16 kVidPhoneAngle = 30;
+static const int16 kReplicatorAngle = 50;
+static const int16 kDrawersAngle = -30;
+static const int16 kCaldoria53Angle = 45;
+static const int16 kCaldoria55Angle = -45;
+
+static const TimeValue kSinclairInterruptionTime1 = 2955;
+static const TimeValue kSinclairInterruptionTime2 = 6835;
+static const TimeValue kSinclairInterruptionTime3 = 9835;
+static const TimeValue kSinclairInterruptionTime4 = 12555;
+
+static const InputBits kPullbackInterruptFilter = kFilterAllInput;
+static const InputBits kRecalibrationInterruptFilter = kFilterAllInput;
+
+static const TimeValue kCaldoriaReplicatorIntroIn = 4933;
+static const TimeValue kCaldoriaReplicatorIntroOut = 6557;
+
+static const TimeValue kCaldoriaReplicatorWrongChoiceIn = 6557;
+static const TimeValue kCaldoriaReplicatorWrongChoiceOut = 8586;
+
+static const TimeValue kCaldoriaReplicatorOJChoiceIn = 8586;
+static const TimeValue kCaldoriaReplicatorOJChoiceOut = 11687;
+
+static const TimeValue kCaldoriaMessagesIntroIn = 11687;
+static const TimeValue kCaldoriaMessagesIntroOut = 13641;
+
+static const TimeValue kCaldoriaFirstMessageIn = 13641;
+static const TimeValue kCaldoriaFirstMessageOut = 14203;
+
+static const TimeValue kCaldoriaSecondMessageIn = 14203;
+static const TimeValue kCaldoriaSecondMessageOut = 14750;
+
+static const TimeValue kCaldoriaDoorCloseIn = 14750;
+static const TimeValue kCaldoriaDoorCloseOut = 15472;
+
+static const TimeValue kCaldoriaElevatorCloseIn = 15472;
+static const TimeValue kCaldoriaElevatorCloseOut = 16336;
+
+static const TimeValue kCaldoriaShowerCloseIn = 16336;
+static const TimeValue kCaldoriaShowerCloseOut = 17101;
+
+static const TimeValue kCaldoriaGTDoorCloseIn = 17101;
+static const TimeValue kCaldoriaGTDoorCloseOut = 18523;
+
+static const TimeValue kCaldoriaNobodyHomeIn = 18523;
+static const TimeValue kCaldoriaNobodyHomeOut = 21469;
+
+static const TimeValue kCaldoriaNoOtherFloorIn = 21469;
+static const TimeValue kCaldoriaNoOtherFloorOut = 28013;
+
+static const TimeValue kCaldoria4DInstructionsIn = 28013;
+static const TimeValue kCaldoria4DInstructionsOut = 29730;
+
+static const TimeValue kCaldoriaDrinkOJIn = 33910;
+static const TimeValue kCaldoriaDrinkOJOut = 35846;
+
+static const TimeValue kCaldoriaNoOtherDestinationIn = 35846;
+static const TimeValue kCaldoriaNoOtherDestinationOut = 37877;
+
+static const TimeValue kCaldoriaUhghIn = 37877;
+static const TimeValue kCaldoriaUhghOut = 38025;
+
+static const TimeValue kCaldoriaSinclairShootsOSIn = 38025;
+static const TimeValue kCaldoriaSinclairShootsOSOut = 40649;
+
+static const TimeValue kCaldoriaScreamingAfterIn = 40649;
+static const TimeValue kCaldoriaScreamingAfterOut = 47661;
+
+static const TimeValue k4FloorTime = 0;
+
+static const TimeValue k4To1Start = 40;
+static const TimeValue k4To1Stop = 7720;
+
+static const TimeValue k4To5Start = 7720;
+static const TimeValue k4To5Stop = 10280;
+
+static const TimeValue k4To2Time = 10280;
+
+static const TimeValue k4To3Time = 10320;
+
+static const TimeValue k1FloorTime = 10360;
+
+static const TimeValue k1To4Start = 10400;
+static const TimeValue k1To4Stop = 18080;
+
+static const TimeValue k1To5Start = 18080;
+static const TimeValue k1To5Stop = 28320;
+
+static const TimeValue k1To2Time = 28320;
+
+static const TimeValue k1To3Time = 28360;
+
+static const TimeValue k5FloorTime = 28400;
+
+static const TimeValue k5To1Start = 28440;
+static const TimeValue k5To1Stop = 38680;
+
+static const TimeValue k5To4Start = 38680;
+static const TimeValue k5To4Stop = 41240;
+
+static const TimeValue k5To2Time = 41240;
+
+static const TimeValue k5To3Time = 41280;
+
+// FuseFunction functions...
+
+const NotificationFlags kSinclairLoopDoneFlag = kLastNeighborhoodNotificationFlag << 1;
+
+SinclairCallBack::SinclairCallBack(Caldoria *caldoria) {
+ _caldoria = caldoria;
+}
+
+void SinclairCallBack::callBack() {
+ _caldoria->checkInterruptSinclair();
+}
+
+Caldoria::Caldoria(InputHandler* nextHandler, PegasusEngine *owner)
+ : Neighborhood(nextHandler, owner, "Caldoria", kCaldoriaID), _sinclairInterrupt(this) {
+ setIsItemTaken(kKeyCard);
+ setIsItemTaken(kOrangeJuiceGlassEmpty);
+ GameState.setTakenItemID(kOrangeJuiceGlassFull, GameState.isTakenItemID(kOrangeJuiceGlassEmpty));
+ _zoomOutSpot = 0;
+ _gunSprite = 0;
+}
+
+Caldoria::~Caldoria() {
+ _sinclairInterrupt.releaseCallBack();
+}
+
+void Caldoria::init() {
+ Neighborhood::init();
+
+ // We need this notification flag as well.
+ _neighborhoodNotification.notifyMe(this, kSinclairLoopDoneFlag, kSinclairLoopDoneFlag);
+
+ _sinclairInterrupt.initCallBack(&_navMovie, kCallBackAtTime);
+
+ forceStridingStop(kCaldoria55, kSouth, kAltCaldoriaSinclairDown);
+ forceStridingStop(kCaldoria50, kNorth, kAltCaldoriaSinclairDown);
+}
+
+void Caldoria::start() {
+ g_energyMonitor->stopEnergyDraining();
+
+ if (!GameState.getCaldoriaSeenPullback()) {
+ _vm->_gfx->doFadeOutSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond);
+
+ g_system->delayMillis(2 * 1000);
+
+ Video::VideoDecoder *pullbackMovie = new Video::QuickTimeDecoder();
+
+ if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.movie"))
+ error("Could not load pullback movie");
+
+ // Draw the first frame so we can fade to it
+ const Graphics::Surface *frame = pullbackMovie->decodeNextFrame();
+ assert(frame);
+ assert(frame->format == g_system->getScreenFormat());
+ g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h);
+ _vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond);
+
+ bool saveAllowed = _vm->swapSaveAllowed(false);
+ bool openAllowed = _vm->swapLoadAllowed(false);
+
+ bool skipped = false;
+ Input input;
+
+ pullbackMovie->start();
+
+ while (!_vm->shouldQuit() && !pullbackMovie->endOfVideo()) {
+ if (pullbackMovie->needsUpdate()) {
+ frame = pullbackMovie->decodeNextFrame();
+
+ if (frame) {
+ g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h);
+ g_system->updateScreen();
+ }
+ }
+
+ InputDevice.getInput(input, kPullbackInterruptFilter);
+ if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested()) {
+ skipped = true;
+ break;
+ }
+
+ g_system->delayMillis(10);
+ }
+
+ delete pullbackMovie;
+
+ if (_vm->shouldQuit())
+ return;
+
+ _vm->swapSaveAllowed(saveAllowed);
+ _vm->swapLoadAllowed(openAllowed);
+
+ ExtraTable::Entry entry;
+
+ if (!skipped) {
+ _vm->_gfx->doFadeOutSync(kThreeSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false);
+ g_system->delayMillis(3 * 1000 / 2);
+ getExtraEntry(kCaldoria00WakeUp1, entry);
+ _navMovie.setTime(entry.movieStart);
+ _navMovie.redrawMovieWorld();
+ _navMovie.show();
+ _vm->refreshDisplay();
+ _vm->_gfx->doFadeInSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false);
+ } else {
+ getExtraEntry(kCaldoria00WakeUp1, entry);
+ _navMovie.setTime(entry.movieStart);
+ _navMovie.redrawMovieWorld();
+ _navMovie.show();
+ }
+
+ GameState.setCaldoriaSeenPullback(true);
+ }
+
+ Neighborhood::start();
+}
+
+void Caldoria::flushGameState() {
+ GameState.setCaldoriaFuseTimeLimit(_utilityFuse.getTimeRemaining());
+}
+
+class AIBombActiveCondition : public AICondition {
+public:
+ AIBombActiveCondition() {}
+
+ bool fireCondition();
+};
+
+// Return true if player is on 53 east and Sinclair is shot.
+bool AIBombActiveCondition::fireCondition() {
+ return GameState.getCurrentRoom() == kCaldoria53 && GameState.getCurrentDirection() == kEast &&
+ GameState.getCaldoriaSinclairShot();
+}
+
+void Caldoria::setUpAIRules() {
+ Neighborhood::setUpAIRules();
+
+ if (g_AIArea) {
+ if (GameState.allTimeZonesFinished()) {
+ AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X49NB1", false);
+ AILocationCondition *locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kCaldoria49, kNorth));
+ AIRule *rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X56EH1", false);
+ AIBombActiveCondition *activeCondition = new AIBombActiveCondition();
+ rule = new AIRule(activeCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+ } else {
+ AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB2", false);
+ AITimerCondition *timerCondition = new AITimerCondition(kLateWarning3TimeLimit, 1, true);
+ AILocationCondition *locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kCaldoria44, kEast));
+ AINotCondition *notCondition = new AINotCondition(locCondition);
+ AIAndCondition *andCondition = new AIAndCondition(timerCondition, notCondition);
+ AIRule *rule = new AIRule(andCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB1", false);
+ timerCondition = new AITimerCondition(kLateWarning2TimeLimit, 1, true);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kCaldoria44, kEast));
+ notCondition = new AINotCondition(locCondition);
+ andCondition = new AIAndCondition(timerCondition, notCondition);
+ rule = new AIRule(andCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XA44EB", false);
+ locCondition = new AILocationCondition(3);
+ locCondition->addLocation(MakeRoomView(kCaldoria01, kNorth));
+ locCondition->addLocation(MakeRoomView(kCaldoria01, kEast));
+ locCondition->addLocation(MakeRoomView(kCaldoria01, kSouth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X42WH1", false);
+ AICondition *condition = makeLocationAndDoesntHaveItemCondition(kCaldoria44, kEast, kKeyCard);
+ rule = new AIRule(condition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ AIActivateRuleAction *ruleAction = new AIActivateRuleAction(rule);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kCaldoria42, kEast));
+ rule = new AIRule(locCondition, ruleAction);
+ g_AIArea->addAIRule(rule);
+ }
+ }
+}
+
+uint16 Caldoria::getDateResID() const {
+ return kDate2318ID;
+}
+
+TimeValue Caldoria::getViewTime(const RoomID room, const DirectionConstant direction) {
+ ExtraTable::Entry extra;
+ uint32 extraID = 0xffffffff;
+
+ switch (room) {
+ case kCaldoria00:
+ if (direction == kEast && _privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag))
+ extraID = k4DEnvironOpenView;
+ break;
+ case kCaldoriaDrawers:
+ if (direction == kNorth && _privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) {
+ if (GameState.isTakenItemID(kKeyCard))
+ extraID = kRightDrawerOpenViewNoKeys;
+ else
+ extraID = kRightDrawerOpenViewWithKeys;
+ }
+ break;
+ case kCaldoria16:
+ if (direction == kSouth && GameState.getCaldoriaSeenSinclairInElevator())
+ extraID = kCaldoria16SouthViewWithElevator;
+ break;
+ case kCaldoriaReplicator:
+ if (GameState.getCaldoriaMadeOJ() && !(GameState.isTakenItemID(kOrangeJuiceGlassEmpty) || GameState.isTakenItemID(kOrangeJuiceGlassFull)))
+ extraID = kReplicatorNorthViewWithOJ;
+ break;
+ case kCaldoriaKiosk:
+ case kCaldoriaBinoculars:
+ return 0xffffffff;
+ case kCaldoria48:
+ if (direction == kNorth && GameState.getCaldoriaRoofDoorOpen())
+ extraID = kCa48NorthExplosion;
+ break;
+ }
+
+ if (extraID == 0xffffffff)
+ return Neighborhood::getViewTime(room, direction);
+
+ getExtraEntry(extraID, extra);
+ return extra.movieEnd - 1;
+}
+
+void Caldoria::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kCaldoria13, kEast):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen13CarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen13CarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ case MakeRoomView(kCaldoria14, kEast):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen14CarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen14CarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ case MakeRoomView(kCaldoria18, kWest):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen18CarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen18CarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ case MakeRoomView(kCaldoria23, kSouth):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen23CarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen23CarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ case MakeRoomView(kCaldoria33, kSouth):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen33CarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen33CarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ case MakeRoomView(kCaldoria36, kNorth):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen36CarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen36CarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ case MakeRoomView(kCaldoria41, kNorth):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41NorthCarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen41NorthCarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ case MakeRoomView(kCaldoria41, kEast):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41EastCarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen41EastCarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ case MakeRoomView(kCaldoria41, kWest):
+ if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41WestCarFlag) && _vm->getRandomBit() == 0) {
+ _privateFlags.setFlag(kCaldoriaPrivateSeen41WestCarFlag, true);
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ }
+ break;
+ default:
+ Neighborhood::startSpotOnceOnly(startTime, stopTime);
+ break;
+ }
+}
+
+void Caldoria::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) {
+ Neighborhood::findSpotEntry(room, direction, flags, entry);
+
+ switch (room) {
+ case kCaldoria00:
+ if (direction == kEast && (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN()))
+ entry.clear();
+ break;
+ case kCaldoriaVidPhone:
+ if (direction == kNorth && GameState.getCaldoriaSeenMessages())
+ entry.clear();
+ break;
+ case kCaldoria44:
+ if (direction == kEast && GameState.getLastRoom() != kCaldoria42)
+ entry.clear();
+ break;
+ }
+}
+
+void Caldoria::startExitMovie(const ExitTable::Entry &exitEntry) {
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria05:
+ case kCaldoria07:
+ if (GameState.getCurrentDirection() == kWest)
+ closeCroppedMovie();
+ // fall through
+ case kCaldoria11:
+ if (GameState.getCurrentDirection() == kEast)
+ closeCroppedMovie();
+ break;
+ case kCaldoria13:
+ case kCaldoria14:
+ if (GameState.getCurrentDirection() == kNorth)
+ closeCroppedMovie();
+ break;
+ }
+
+ Neighborhood::startExitMovie(exitEntry);
+}
+
+void Caldoria::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria12:
+ if (GameState.getCurrentDirection() == kNorth)
+ closeCroppedMovie();
+ break;
+ }
+
+ Neighborhood::startZoomMovie(zoomEntry);
+}
+
+void Caldoria::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) {
+ if (GameState.getCurrentRoom() == kCaldoria27 || GameState.getCurrentRoom() == kCaldoria28 || GameState.getCurrentRoom() == kCaldoria45)
+ // Must be opening elevator door.
+ closeCroppedMovie();
+
+ if (GameState.getCurrentRoom() == kCaldoria44 && GameState.getLastRoom() != kCaldoria42)
+ startExtraSequence(kArriveAtCaldoriaFromTSA, kDoorOpenCompletedFlag, false);
+ else
+ Neighborhood::startDoorOpenMovie(startTime, stopTime);
+}
+
+void Caldoria::startTurnPush(const TurnDirection turnDirection, const TimeValue newViewTime, const DirectionConstant destDirection) {
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria05:
+ case kCaldoria07:
+ if (GameState.getCurrentDirection() == kWest)
+ closeCroppedMovie();
+ break;
+ case kCaldoria11:
+ if (GameState.getCurrentDirection() == kEast)
+ closeCroppedMovie();
+ break;
+ case kCaldoria12:
+ case kCaldoria13:
+ case kCaldoria14:
+ case kCaldoria27:
+ case kCaldoria28:
+ case kCaldoria45:
+ if (GameState.getCurrentDirection() == kNorth)
+ closeCroppedMovie();
+ break;
+ case kCaldoria48:
+ if (_croppedMovie.isSurfaceValid())
+ closeCroppedMovie();
+ break;
+ }
+
+ Neighborhood::startTurnPush(turnDirection, newViewTime, destDirection);
+}
+
+void Caldoria::bumpIntoWall() {
+ requestSpotSound(kCaldoriaUhghIn, kCaldoriaUhghOut, kFilterNoInput, 0);
+ Neighborhood::bumpIntoWall();
+}
+
+void Caldoria::closeDoorOffScreen(const RoomID room, const DirectionConstant direction) {
+ switch (room) {
+ case kCaldoria08:
+ if (direction == kNorth)
+ playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut);
+ else
+ playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut);
+ break;
+ case kCaldoria09:
+ playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut);
+ break;
+ case kCaldoria16:
+ case kCaldoria38:
+ case kCaldoria46:
+ case kCaldoria27:
+ case kCaldoria28:
+ case kCaldoria45:
+ playSpotSoundSync(kCaldoriaElevatorCloseIn, kCaldoriaElevatorCloseOut);
+ break;
+ case kCaldoria44:
+ case kCaldoria42:
+ if (GameState.getCurrentRoom() == kCaldoria42)
+ playSpotSoundSync(kCaldoriaGTDoorCloseIn, kCaldoriaGTDoorCloseOut);
+ break;
+ default:
+ playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut);
+ break;
+ }
+}
+
+int16 Caldoria::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
+ int16 result = Neighborhood::getStaticCompassAngle(room, dir);
+
+ switch (room) {
+ case kCaldoriaVidPhone:
+ result += kVidPhoneAngle;
+ break;
+ case kCaldoriaReplicator:
+ result += kReplicatorAngle;
+ break;
+ case kCaldoriaDrawers:
+ result += kDrawersAngle;
+ break;
+ case kCaldoria53:
+ result += kCaldoria53Angle;
+ break;
+ case kCaldoria55:
+ result += kCaldoria55Angle;
+ break;
+ }
+
+ return result;
+}
+
+void Caldoria::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) {
+ Neighborhood::getExitCompassMove(exitEntry, compassMove);
+
+ switch (MakeRoomView(exitEntry.room, exitEntry.direction)) {
+ case MakeRoomView(kCaldoria08, kNorth):
+ case MakeRoomView(kCaldoria09, kSouth):
+ compassMove.insertFaderKnot((exitEntry.movieStart + exitEntry.movieEnd) >> 1, compassMove.getNthKnotValue(0) + 30);
+ break;
+ case MakeRoomView(kCaldoria10, kEast):
+ compassMove.insertFaderKnot(exitEntry.movieStart + 4 * kCaldoriaFrameDuration, 90);
+ compassMove.insertFaderKnot(exitEntry.movieStart + 19 * kCaldoriaFrameDuration, -90);
+ break;
+ case MakeRoomView(kCaldoria42, kWest):
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, -90, exitEntry.movieEnd, 90);
+ compassMove.insertFaderKnot(exitEntry.movieStart + 3 * kCaldoriaFrameDuration, -90);
+ compassMove.insertFaderKnot(exitEntry.movieStart + 33 * kCaldoriaFrameDuration, 90);
+ break;
+ case MakeRoomView(kCaldoria54, kEast):
+ if (getCurrentAlternate() != kAltCaldoriaSinclairDown) {
+ compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kCaldoriaFrameDuration, 135);
+ compassMove.insertFaderKnot(exitEntry.movieEnd, 135);
+ }
+ break;
+ case MakeRoomView(kCaldoria55, kNorth):
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, 315, exitEntry.movieEnd, 270);
+ break;
+ }
+}
+
+void Caldoria::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) {
+ Neighborhood::getZoomCompassMove(zoomEntry, compassMove);
+
+ switch (zoomEntry.hotspot) {
+ case kCaBathroomToiletSpotID:
+ compassMove.insertFaderKnot(zoomEntry.movieStart + 4 * kCaldoriaFrameDuration, 90);
+ compassMove.insertFaderKnot(zoomEntry.movieStart + 19 * kCaldoriaFrameDuration, -90);
+ compassMove.insertFaderKnot(zoomEntry.movieEnd, -90);
+ break;
+ }
+}
+
+void Caldoria::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) {
+ switch (entry.extra) {
+ case kCaldoria00WakeUp1:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 180);
+ compassMove.insertFaderKnot(entry.movieStart + 1000, 90);
+ compassMove.insertFaderKnot(entry.movieStart + 1640, 120);
+ compassMove.insertFaderKnot(entry.movieStart + 2240, 135);
+ compassMove.insertFaderKnot(entry.movieStart + 2640, 180);
+ break;
+ case kCaldoria00WakeUp2:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 180, entry.movieEnd, 90);
+ compassMove.insertFaderKnot(entry.movieStart + 560, 90);
+ break;
+ case kCaldoria56BombStage1:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 10);
+ compassMove.insertFaderKnot(entry.movieStart + 31 * kCaldoriaFrameDuration, 60);
+ compassMove.insertFaderKnot(entry.movieStart + 49 * kCaldoriaFrameDuration, 60);
+ compassMove.insertFaderKnot(entry.movieStart + 66 * kCaldoriaFrameDuration, 10);
+ break;
+ case kCaldoria56BombStage7:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 10, entry.movieEnd, 90);
+ compassMove.insertFaderKnot(entry.movieStart + 131 * kCaldoriaFrameDuration, 10);
+ compassMove.insertFaderKnot(entry.movieStart + 148 * kCaldoriaFrameDuration, 60);
+ compassMove.insertFaderKnot(entry.movieStart + 165 * kCaldoriaFrameDuration, 60);
+ compassMove.insertFaderKnot(entry.movieEnd - 5 * kCaldoriaFrameDuration, 90);
+ break;
+ default:
+ Neighborhood::getExtraCompassMove(entry, compassMove);
+ break;
+ }
+}
+
+void Caldoria::loadAmbientLoops() {
+ RoomID room = GameState.getCurrentRoom();
+
+ if (room == kCaldoria00 && GameState.getCaldoriaWokenUp())
+ loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4);
+ else if (room >= kCaldoria01 && room <= kCaldoria14)
+ loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4);
+ else if (room == kCaldoria27 || room == kCaldoria28 || room == kCaldoria45)
+ loadLoopSound1("Sounds/Caldoria/Elevator Loop.AIFF", 0x100 / 5);
+ else if (room == kCaldoria44)
+ loadLoopSound1("Sounds/Caldoria/TSA Hum Loop.AIFF");
+ else if (room >= kCaldoria15 && room <= kCaldoria48)
+ loadLoopSound1("Sounds/Caldoria/Industrial Nuage.aiff", 2 * 0x100 / 3);
+ else if (room >= kCaldoria49 && room <= kCaldoria56)
+ loadLoopSound1("Sounds/Caldoria/A50NLB00.22K.AIFF", 0x100 / 4);
+}
+
+void Caldoria::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kCaldoria06, kSouth):
+ case MakeRoomView(kCaldoria13, kNorth):
+ case MakeRoomView(kCaldoria16, kSouth):
+ case MakeRoomView(kCaldoria38, kEast):
+ case MakeRoomView(kCaldoria38, kWest):
+ case MakeRoomView(kCaldoria40, kNorth):
+ case MakeRoomView(kCaldoria44, kEast):
+ case MakeRoomView(kCaldoria48, kNorth):
+ case MakeRoomView(kCaldoria49, kNorth):
+ makeContinuePoint();
+ break;
+ }
+}
+
+void Caldoria::spotCompleted() {
+ Neighborhood::spotCompleted();
+ if (GameState.getCurrentRoom() == kCaldoriaBinoculars)
+ startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) {
+ switch (room) {
+ case kCaldoria56:
+ if (!GameState.getCaldoriaGunAimed())
+ // Fall through...
+ case kCaldoria49:
+ case kCaldoria50:
+ case kCaldoria51:
+ case kCaldoria52:
+ case kCaldoria53:
+ case kCaldoria54:
+ case kCaldoria55:
+ if (GameState.getCaldoriaSinclairShot())
+ setCurrentAlternate(kAltCaldoriaSinclairDown);
+ break;
+ }
+
+ Neighborhood::arriveAt(room, direction);
+ Input dummy;
+
+ switch (room) {
+ case kCaldoria00:
+ arriveAtCaldoria00();
+ break;
+ case kCaldoria05:
+ if (direction == kWest && GameState.getCaldoriaINNAnnouncing())
+ loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop);
+ break;
+ case kCaldoria07:
+ if (direction == kWest && GameState.getCaldoriaINNAnnouncing())
+ loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop);
+ break;
+ case kCaldoria09:
+ _lastExtra = 0xffffffff;
+ break;
+ case kCaldoriaToilet:
+ GameState.setScoringReadPaper(true);
+ break;
+ case kCaldoriaReplicator:
+ setCurrentActivation(kActivateReplicatorReady);
+ requestSpotSound(kCaldoriaReplicatorIntroIn, kCaldoriaReplicatorIntroOut, kFilterNoInput, 0);
+ break;
+ case kCaldoria11:
+ setCurrentAlternate(kAltCaldoriaNormal);
+ if (direction == kEast && !GameState.getCaldoriaSeenMessages())
+ loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop);
+ break;
+ case kCaldoria12:
+ if (direction == kNorth && !GameState.getCaldoriaSeenMessages())
+ loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop);
+ break;
+ case kCaldoriaDrawers:
+ setCurrentActivation(kActivateDrawersClosed);
+ break;
+ case kCaldoria13:
+ GameState.setCaldoriaINNAnnouncing(true);
+ if (direction == kNorth && !GameState.getCaldoriaSeenMessages())
+ loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop);
+ break;
+ case kCaldoria14:
+ if (direction == kNorth && !GameState.getCaldoriaSeenMessages())
+ loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop);
+ break;
+ case kCaldoria08:
+ if (direction == kWest)
+ setCurrentActivation(kActivateMirrorReady);
+ // Fall through...
+ case kCaldoria15:
+ GameState.setCaldoriaINNAnnouncing(true);
+ break;
+ case kCaldoria27:
+ case kCaldoria28:
+ case kCaldoria45:
+ if (GameState.getCurrentDirection() == kNorth)
+ openDoor();
+ break;
+ case kCaldoriaBinoculars:
+ GameState.setScoringLookThroughTelescope(true);
+ break;
+ case kCaldoriaKiosk:
+ GameState.setScoringSawCaldoriaKiosk(true);
+ startExtraSequenceSync(kCaldoriaKioskVideo, kFilterAllInput);
+ downButton(dummy);
+ break;
+ case kCaldoria44:
+ arriveAtCaldoria44();
+ break;
+ case kCaldoria49:
+ arriveAtCaldoria49();
+ break;
+ case kCaldoria53:
+ if (direction == kEast && !GameState.getCaldoriaSinclairShot())
+ zoomToSinclair();
+ break;
+ case kCaldoria50:
+ if (direction == kNorth && !GameState.getCaldoriaSinclairShot())
+ setUpSinclairLoops();
+ break;
+ case kCaldoria54:
+ if (direction == kSouth && !GameState.getCaldoriaSinclairShot())
+ setUpSinclairLoops();
+ break;
+ case kCaldoria56:
+ arriveAtCaldoria56();
+ break;
+ case kCaldoriaDeathRoom:
+ arriveAtCaldoriaDeath();
+ break;
+ }
+
+ checkSinclairShootsOS();
+ setUpRoofTop();
+}
+
+void Caldoria::doAIRecalibration() {
+ GameState.setCaldoriaDidRecalibration(true);
+
+ if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB1", true, kRecalibrationInterruptFilter))
+ return;
+
+ g_interface->calibrateEnergyBar();
+ if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB4", true, kRecalibrationInterruptFilter))
+ return;
+
+ g_interface->raiseInventoryDrawerSync();
+ if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB6", true, kRecalibrationInterruptFilter)) {
+ g_interface->lowerInventoryDrawerSync();
+ return;
+ }
+
+ g_interface->lowerInventoryDrawerSync();
+ g_interface->raiseBiochipDrawerSync();
+
+ if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB5", true, kRecalibrationInterruptFilter)) {
+ g_interface->lowerBiochipDrawerSync();
+ return;
+ }
+
+ g_interface->lowerBiochipDrawerSync();
+
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB8", false, kRecalibrationInterruptFilter);
+}
+
+void Caldoria::arriveAtCaldoria00() {
+ if (GameState.getCurrentDirection() == kEast) {
+ if (GameState.getCaldoriaWokenUp()) {
+ if (!GameState.getCaldoriaDidRecalibration())
+ doAIRecalibration();
+ setCurrentActivation(kActivate4DClosed);
+ } else {
+ // Good morning, sleeping beauty
+ ExtraTable::Entry extra;
+ getExtraEntry(kCaldoria00WakeUp1, extra);
+
+ if (_navMovie.getTime() != extra.movieStart) {
+ _navMovie.setTime(extra.movieStart);
+ _navMovie.redrawMovieWorld();
+ }
+
+ startExtraSequenceSync(kCaldoria00WakeUp1, kFilterNoInput);
+ GameState.setCaldoriaWokenUp(true);
+ playCroppedMovieOnce("Images/Caldoria/VidPhone.movie", kCaldoriaVidPhoneLeft, kCaldoriaVidPhoneTop, kFilterAllInput);
+ startExtraSequence(kCaldoria00WakeUp2, kExtraCompletedFlag, kFilterNoInput);
+ }
+ }
+}
+
+bool Caldoria::wantsCursor() {
+ return GameState.getCaldoriaDidRecalibration();
+}
+
+void Caldoria::arriveAtCaldoria44() {
+ if (GameState.getLastNeighborhood() != kCaldoriaID) {
+ openDoor();
+ } else {
+ setCurrentActivation(kActivateReadyForCard);
+ loopExtraSequence(kCaldoriaTransporterArrowLoop, 0);
+ }
+}
+
+void Caldoria::arriveAtCaldoria49() {
+ if (GameState.getLastRoom() == kCaldoria48)
+ setCurrentAlternate(kAltCaldoriaNormal);
+
+ // Need to force the loop to play.
+ if (GameState.getCurrentDirection() == kNorth) {
+ GameState.setCaldoriaFuseTimeLimit(kSinclairShootsTimeLimit);
+ startExtraSequence(kCa49NorthVoiceAnalysis, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+void Caldoria::arriveAtCaldoria56() {
+ if (!GameState.getCaldoriaBombDisarmed()) {
+ _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, true);
+
+ if (GameState.getCurrentDirection() == kNorth) {
+ turnRight();
+ } else if (GameState.getCurrentDirection() == kSouth) {
+ turnLeft();
+ } else if (GameState.getCurrentDirection() == kEast) {
+ _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false);
+ newInteraction(kCaldoriaBombInteractionID);
+ }
+ }
+}
+
+void Caldoria::arriveAtCaldoriaDeath() {
+ if (GameState.getLastRoom() == kCaldoria49) {
+ if (GameState.getCaldoriaSinclairShot()) {
+ die(kDeathNuclearExplosion);
+ } else {
+ playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut);
+ playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut);
+ die(kDeathSinclairShotDelegate);
+ }
+ } else {
+ die(kDeathShotBySinclair);
+ }
+}
+
+void Caldoria::setUpRoofTop() {
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria48:
+ if (GameState.getCurrentDirection() == kNorth) {
+ if (GameState.getCaldoriaRoofDoorOpen()) {
+ setCurrentAlternate(kAltCaldoriaRoofDoorBlown);
+ } else if (GameState.getCaldoriaDoorBombed()) {
+ // Long enough for AI hints...?
+ _utilityFuse.primeFuse(kCardBombCountDownTime);
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::doorBombTimerExpired));
+ _utilityFuse.lightFuse();
+
+ loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop);
+ } else {
+ setCurrentActivation(kActivateRoofSlotEmpty);
+ }
+ }
+ break;
+ case kCaldoria56:
+ if (GameState.getCurrentDirection() == kEast && GameState.getCaldoriaGunAimed())
+ startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false);
+ else
+ // Fall through...
+ case kCaldoria49:
+ case kCaldoria50:
+ case kCaldoria51:
+ case kCaldoria52:
+ case kCaldoria53:
+ case kCaldoria54:
+ case kCaldoria55:
+ if (!GameState.getCaldoriaSinclairShot()) {
+ if (GameState.getCaldoriaSawVoiceAnalysis() && !_utilityFuse.isFuseLit()) {
+ _utilityFuse.primeFuse(GameState.getCaldoriaFuseTimeLimit());
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::sinclairTimerExpired));
+ _utilityFuse.lightFuse();
+ }
+ } else {
+ setCurrentAlternate(kAltCaldoriaSinclairDown);
+ }
+ break;
+ }
+}
+
+void Caldoria::downButton(const Input &input) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kCaldoria01, kEast):
+ GameState.setCaldoriaWokenUp(true);
+ startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ Neighborhood::downButton(input);
+ break;
+ }
+}
+
+void Caldoria::turnTo(const DirectionConstant direction) {
+ Neighborhood::turnTo(direction);
+
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria00:
+ if (direction == kEast)
+ setCurrentActivation(kActivate4DClosed);
+ break;
+ case kCaldoria01:
+ if (direction == kEast) {
+ GameState.setCaldoriaWokenUp(true);
+ startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kCaldoria05:
+ if (direction == kWest && GameState.getCaldoriaINNAnnouncing())
+ loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop);
+ break;
+ case kCaldoria07:
+ if (direction == kWest && GameState.getCaldoriaINNAnnouncing())
+ loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop);
+ break;
+ case kCaldoria08:
+ if (direction == kWest)
+ setCurrentActivation(kActivateMirrorReady);
+ break;
+ case kCaldoria09:
+ _lastExtra = 0xffffffff;
+ break;
+ case kCaldoria11:
+ if (direction == kEast && !GameState.getCaldoriaSeenMessages())
+ loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop);
+ break;
+ case kCaldoria12:
+ if (direction == kNorth && !GameState.getCaldoriaSeenMessages())
+ loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop);
+ break;
+ case kCaldoria13:
+ if (direction == kNorth && !GameState.getCaldoriaSeenMessages())
+ loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop);
+ break;
+ case kCaldoria14:
+ if (direction == kNorth && !GameState.getCaldoriaSeenMessages())
+ loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop);
+ break;
+ case kCaldoria27:
+ case kCaldoria28:
+ case kCaldoria45:
+ if (direction == kNorth)
+ openElevatorMovie();
+ else
+ closeCroppedMovie();
+ break;
+ case kCaldoria48:
+ if (direction == kNorth && !GameState.getCaldoriaDoorBombed())
+ setCurrentActivation(kActivateRoofSlotEmpty);
+ break;
+ case kCaldoria53:
+ if (GameState.getCurrentDirection() == kEast && !GameState.getCaldoriaSinclairShot())
+ zoomToSinclair();
+ break;
+ case kCaldoria50:
+ if (direction == kNorth && !GameState.getCaldoriaSinclairShot())
+ setUpSinclairLoops();
+ break;
+ case kCaldoria54:
+ if (direction == kSouth && !GameState.getCaldoriaSinclairShot())
+ setUpSinclairLoops();
+ break;
+ case kCaldoria56:
+ if (_privateFlags.getFlag(kCaldoriaPrivateZoomingToBombFlag)) {
+ _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false);
+ newInteraction(kCaldoriaBombInteractionID);
+ } else if (GameState.getCaldoriaBombDisarmed()) {
+ _vm->playEndMessage();
+ }
+ break;
+ }
+
+ checkSinclairShootsOS();
+}
+
+void Caldoria::zoomTo(const Hotspot *zoomOutSpot) {
+ // Need to set _zoomOutSpot here because we may come through
+ // this function another way, say by pressing the down arrow,
+ // that doesn't involve the ClickInHotSpot function.
+ _zoomOutSpot = zoomOutSpot;
+
+ if (zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) {
+ if (_privateFlags.getFlag(kCaloriaPrivateLeftDrawerOpenFlag)) {
+ _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false);
+ startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput);
+ } else if (_privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) {
+ _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false);
+ if (GameState.isTakenItemID(kKeyCard))
+ startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, false);
+ else
+ startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, false);
+ } else {
+ Neighborhood::zoomTo(zoomOutSpot);
+ }
+ } else {
+ Neighborhood::zoomTo(zoomOutSpot);
+ }
+}
+
+void Caldoria::setUpSinclairLoops() {
+ _navMovie.stop();
+ scheduleNavCallBack(kSinclairLoopDoneFlag);
+ _sinclairLoopCount = 0;
+ _numSinclairLoops = 2;
+ _navMovie.start();
+}
+
+void Caldoria::zoomToSinclair() {
+ _utilityFuse.stopFuse();
+ _privateFlags.setFlag(kCaldoriaPrivateReadyToShootFlag, true);
+ setCurrentActivation(kActivateZoomedOnSinclair);
+
+ ExtraTable::Entry entry;
+ getExtraEntry(kCa53EastZoomToSinclair, entry);
+ _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime1, _navMovie.getScale());
+ startExtraSequence(kCa53EastZoomToSinclair, kExtraCompletedFlag, kFilterAllInput);
+}
+
+void Caldoria::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ Neighborhood::receiveNotification(notification, flags);
+
+ if ((flags & kExtraCompletedFlag) != 0) {
+ InventoryItem *item;
+ _interruptionFilter = kFilterAllInput;
+
+ switch (_lastExtra) {
+ case kCaldoria00WakeUp2:
+ makeContinuePoint();
+ // Force ArriveAt to do its thing...
+ GameState.setCurrentRoom(kNoRoomID);
+ arriveAt(kCaldoria00, kEast);
+ break;
+ case k4DEnvironOpenToINN:
+ GameState.setCaldoriaSeenINN(true);
+ GameState.setScoringSawINN(true);
+ // Fall through to k4DEnvironOpen...
+ case k4DEnvironOpen:
+ _privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, true);
+ setCurrentActivation(kActivate4DOpen);
+ newInteraction(kCaldoria4DInteractionID);
+ break;
+ case kCaldoriaShowerUp:
+ GameState.setScoringTookShower(true);
+ GameState.setCaldoriaDoneHygiene(true);
+ break;
+ case kLeftDrawerClose:
+ case kRightDrawerCloseNoKeys:
+ case kRightDrawerCloseWithKeys:
+ if (_zoomOutSpot && _zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) {
+ Input input;
+ clickInHotspot(input, _zoomOutSpot);
+ }
+ break;
+ case kCreateOrangeJuice:
+ setCurrentActivation(kActivateOJOnThePad);
+ requestSpotSound(kCaldoriaReplicatorOJChoiceIn, kCaldoriaReplicatorOJChoiceOut, kFilterNoInput, 0);
+ break;
+ case kCaldoria00SitDown:
+ arriveAt(kCaldoria00, kEast);
+ break;
+ case kCaldoria16ElevatorUp:
+ startExtraSequence(kCaldoria16ElevatorDown, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoria16ElevatorDown:
+ GameState.setCaldoriaSeenSinclairInElevator(true);
+ _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true);
+ openDoor();
+ break;
+ case kCaldoriaFourthToGround:
+ case kCaldoriaRoofToGround:
+ arriveAt(kCaldoria28, GameState.getCurrentDirection());
+ break;
+ case kCaldoriaFourthToRoof:
+ case kCaldoriaGroundToRoof:
+ arriveAt(kCaldoria45, GameState.getCurrentDirection());
+ break;
+ case kCaldoriaGroundToFourth:
+ case kCaldoriaRoofToFourth:
+ arriveAt(kCaldoria27, GameState.getCurrentDirection());
+ break;
+ case kCaGTCardSwipe:
+ item = (InventoryItem *)_vm->getAllItems().findItemByID(kKeyCard);
+ _vm->addItemToInventory(item);
+ setCurrentActivation(kActivateReadyToTransport);
+ break;
+ case kCaGTFryTheFly:
+ case kCaGTGoToTSA:
+ _vm->jumpToNewEnvironment(kFullTSAID, kTSA00, kNorth);
+ break;
+ case kCaGTGoToTokyo:
+ playDeathExtra(kCaGTArriveAtTokyo, kDeathUncreatedInCaldoria);
+ break;
+ case kCaGTGoToBeach:
+ playDeathExtra(kCaGTArriveAtBeach, kDeathUncreatedInCaldoria);
+ break;
+ case kCa48NorthExplosion:
+ // Current biochip must be the shield if we got here.
+ _vm->getCurrentBiochip()->setItemState(kShieldNormal);
+ break;
+ case kBinocularsZoomInOnShip:
+ setCurrentActivation(kActivateFocusedOnShip);
+ break;
+ case kCa49NorthVoiceAnalysis:
+ _utilityFuse.primeFuse(kSinclairShootsTimeLimit);
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::sinclairTimerExpired));
+ _utilityFuse.lightFuse();
+ GameState.setCaldoriaSawVoiceAnalysis(true);
+ break;
+ case kCa53EastZoomToSinclair:
+ if (GameState.getCaldoriaSinclairShot()) {
+ delete _gunSprite;
+ _gunSprite = 0;
+ startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false);
+ } else {
+ playDeathExtra(kCa53EastDeath2, kDeathSinclairShotDelegate);
+ }
+ break;
+ case kCa53EastShootSinclair:
+ _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kStunGun));
+ startExtraSequence(kCa53EastZoomOutFromSinclair, kExtraCompletedFlag, false);
+ GameState.setScoringStunnedSinclair(true);
+ break;
+ case kCa53EastZoomOutFromSinclair:
+ setCurrentAlternate(kAltCaldoriaSinclairDown);
+ updateViewFrame();
+ makeContinuePoint();
+ break;
+ }
+ } else if ((flags & kSpotSoundCompletedFlag) != 0) {
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria20:
+ case kCaldoria21:
+ case kCaldoria26:
+ case kCaldoria29:
+ case kCaldoria34:
+ case kCaldoria35:
+ updateViewFrame();
+ break;
+ case kCaldoria27:
+ case kCaldoria28:
+ case kCaldoria45:
+ updateElevatorMovie();
+ break;
+ case kCaldoriaReplicator:
+ emptyOJGlass();
+ break;
+ }
+ } else if ((flags & kSinclairLoopDoneFlag) != 0) {
+ if (++_sinclairLoopCount == _numSinclairLoops) {
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria50:
+ playDeathExtra(kCa50SinclairShoots, kDeathShotBySinclair);
+ break;
+ case kCaldoria54:
+ playDeathExtra(kCa54SouthDeath, kDeathShotBySinclair);
+ break;
+ }
+ } else {
+ _navMovie.stop();
+ scheduleNavCallBack(kSinclairLoopDoneFlag);
+ _navMovie.start();
+ }
+ }
+
+ g_AIArea->checkMiddleArea();
+}
+
+InputBits Caldoria::getInputFilter() {
+ InputBits result = Neighborhood::getInputFilter();
+
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria00:
+ if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag))
+ result &= ~kFilterAllDirections;
+ break;
+ case kCaldoriaBinoculars:
+ if (getCurrentActivation() == kActivateNotFocusedOnShip)
+ result &= ~(kFilterDownButton | kFilterDownAuto);
+ break;
+ case kCaldoria53:
+ if (_privateFlags.getFlag(kCaldoriaPrivateReadyToShootFlag) && !GameState.getCaldoriaSinclairShot())
+ result &= ~kFilterAllDirections;
+ break;
+ case kCaldoria48:
+ if (GameState.getCaldoriaDoorBombed())
+ result &= ~kFilterAllDirections;
+ }
+
+ return result;
+}
+
+void Caldoria::activateHotspots() {
+ Neighborhood::activateHotspots();
+
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoriaDrawers:
+ if (getCurrentActivation() == kActivateRightOpen) {
+ if (GameState.isTakenItemID(kKeyCard)) {
+ _vm->getAllHotspots().activateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID);
+ } else {
+ _vm->getAllHotspots().activateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID);
+ }
+ }
+ case kCaldoriaReplicator:
+ if (GameState.getCaldoriaMadeOJ())
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaMakeOJSpotID);
+ break;
+ case kCaldoria27:
+ if (GameState.isCurrentDoorOpen()) {
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator1);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator2);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator3);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator4);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator5);
+ }
+ break;
+ case kCaldoria28:
+ if (GameState.isCurrentDoorOpen()) {
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator1);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator2);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator3);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator4);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator5);
+ }
+ break;
+ case kCaldoria45:
+ if (GameState.isCurrentDoorOpen()) {
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator1);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator2);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator3);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator4);
+ _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator5);
+ }
+ break;
+ }
+}
+
+void Caldoria::clickInHotspot(const Input &input, const Hotspot *spot) {
+ switch (spot->getObjectID()) {
+ case kCa4DEnvironOpenSpotID:
+ if (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN()) {
+ startExtraSequence(k4DEnvironOpen, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ // This trick depends on the following sequences being in order in the
+ // world movie:
+ // k4DEnvironOpenToINN
+ // k4DINNInterruption
+ // k4DINNIntro
+ // k4DINNMarkJohnson
+ // k4DINNMeganLove
+ // k4DINNFadeOut
+ // k4DEnvironOpenFromINN
+ loadLoopSound1("");
+ loadLoopSound2("");
+ startExtraLongSequence(k4DEnvironOpenToINN, k4DEnvironOpenFromINN, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kCa4DEnvironCloseSpotID:
+ ((Caldoria4DSystem *)_currentInteraction)->shutDown4DSystem();
+ break;
+ case kCaBathroomMirrorSpotID:
+ newInteraction(kCaldoriaMirrorInteractionID);
+ break;
+ case kCaShowerSpotID:
+ requestExtraSequence(kCaldoriaShowerTitle, 0, kFilterNoInput);
+ requestExtraSequence(kCaldoriaShowerButton, 0, kFilterNoInput);
+ requestExtraSequence(kCaldoriaShowerDown, 0, kFilterNoInput);
+ requestExtraSequence(kCaldoriaShowerUp, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaLeftDrawerOpenSpotID:
+ _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, true);
+ setCurrentActivation(kActivateLeftOpen);
+ startExtraSequence(kLeftDrawerOpen, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaLeftDrawerCloseSpotID:
+ _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false);
+ setCurrentActivation(kActivateDrawersClosed);
+ startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaRightDrawerOpenSpotID:
+ _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, true);
+ setCurrentActivation(kActivateRightOpen);
+ if (GameState.isTakenItemID(kKeyCard))
+ startExtraSequence(kRightDrawerOpenNoKeys, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kRightDrawerOpenWithKeys, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaRightDrawerWithKeysCloseSpotID:
+ _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false);
+ setCurrentActivation(kActivateDrawersClosed);
+ startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaRightDrawerNoKeysCloseSpotID:
+ _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false);
+ setCurrentActivation(kActivateDrawersClosed);
+ startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaMakeStickyBunsSpotID:
+ requestSpotSound(kCaldoriaReplicatorWrongChoiceIn, kCaldoriaReplicatorWrongChoiceOut, kFilterNoInput, 0);
+ break;
+ case kCaldoriaMakeOJSpotID:
+ GameState.setCaldoriaMadeOJ(true);
+ startExtraSequence(kCreateOrangeJuice, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaBedroomVidPhoneActivationSpotID:
+ newInteraction(kCaldoriaMessagesInteractionID);
+ break;
+ case kCaldoriaFourthFloorElevatorSpotID:
+ if (!GameState.getCaldoriaSeenSinclairInElevator()) {
+ startExtraSequence(kCaldoria16ElevatorUp, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true);
+ openDoor();
+ }
+ break;
+ case kCaldoriaGroundElevatorSpotID:
+ _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true);
+ openDoor();
+ break;
+ case kCaldoriaRoofElevatorSpotID:
+ _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true);
+ openDoor();
+ break;
+ case kCaldoriaFourthFloorElevator1:
+ case kCaldoriaFourthFloorElevator2:
+ case kCaldoriaFourthFloorElevator3:
+ case kCaldoriaFourthFloorElevator4:
+ case kCaldoriaFourthFloorElevator5:
+ // Assumes that elevator hot spots are consecutive.
+ takeElevator(4, spot->getObjectID() - kCaldoriaFourthFloorElevator1 + 1);
+ break;
+ case kCaldoriaGroundElevator1:
+ case kCaldoriaGroundElevator2:
+ case kCaldoriaGroundElevator3:
+ case kCaldoriaGroundElevator4:
+ case kCaldoriaGroundElevator5:
+ // Assumes that elevator hot spots are consecutive.
+ takeElevator(1, spot->getObjectID() - kCaldoriaGroundElevator1 + 1);
+ break;
+ case kCaldoriaRoofElevator1:
+ case kCaldoriaRoofElevator2:
+ case kCaldoriaRoofElevator3:
+ case kCaldoriaRoofElevator4:
+ case kCaldoriaRoofElevator5:
+ // Assumes that elevator hot spots are consecutive.
+ takeElevator(5, spot->getObjectID() - kCaldoriaRoofElevator1 + 1);
+ break;
+ case kCaldoriaGTTokyoSpotID:
+ startExtraSequence(kCaGTGoToTokyo, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaGTTSASpotID:
+ GameState.setScoringGoToTSA(true);
+ startExtraLongSequence(kCaGTFryTheFly, kCaGTGoToTSA, kExtraCompletedFlag, false);
+ break;
+ case kCaldoriaGTBeachSpotID:
+ startExtraSequence(kCaGTGoToBeach, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaGTOtherSpotID:
+ showExtraView(kCaGTOtherChoice);
+ playSpotSoundSync(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut);
+ showExtraView(kCaGTCardSwipe);
+ break;
+ case kCaldoriaZoomInOnShipSpotID:
+ startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoriaRoofDoorSpotID:
+ startExtraSequence(kCa48NorthRooftopClosed, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaldoria20DoorbellSpotID:
+ case kCaldoria21DoorbellSpotID:
+ case kCaldoria26DoorbellSpotID:
+ case kCaldoria29DoorbellSpotID:
+ case kCaldoria34DoorbellSpotID:
+ case kCaldoria35DoorbellSpotID:
+ clickOnDoorbell(spot->getObjectID());
+ break;
+ default:
+ Neighborhood::clickInHotspot(input, spot);
+ break;
+ }
+}
+
+void Caldoria::clickOnDoorbell(const HotSpotID doorBellSpotID) {
+ uint32 extra;
+ ExtraTable::Entry entry;
+
+ switch (doorBellSpotID) {
+ case kCaldoria20DoorbellSpotID:
+ extra = kCaldoria20Doorbell;
+ break;
+ case kCaldoria21DoorbellSpotID:
+ extra = kCaldoria21Doorbell;
+ break;
+ case kCaldoria26DoorbellSpotID:
+ extra = kCaldoria26Doorbell;
+ break;
+ case kCaldoria29DoorbellSpotID:
+ extra = kCaldoria29Doorbell;
+ break;
+ case kCaldoria34DoorbellSpotID:
+ extra = kCaldoria34Doorbell;
+ break;
+ case kCaldoria35DoorbellSpotID:
+ extra = kCaldoria35Doorbell;
+ break;
+ default:
+ error("Invalid doorbell hotspot");
+ }
+
+ getExtraEntry(extra, entry);
+ showViewFrame(entry.movieStart);
+ requestSpotSound(kCaldoriaNobodyHomeIn, kCaldoriaNobodyHomeOut, kFilterNoInput, kSpotSoundCompletedFlag);
+}
+
+CanOpenDoorReason Caldoria::canOpenDoor(DoorTable::Entry &entry) {
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria16:
+ case kCaldoria38:
+ case kCaldoria46:
+ if (GameState.getCurrentDirection() == kSouth && !_privateFlags.getFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag))
+ return kCantOpenLocked;
+ break;
+ }
+
+ return Neighborhood::canOpenDoor(entry);
+}
+
+void Caldoria::doorOpened() {
+ Neighborhood::doorOpened();
+ _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, false);
+}
+
+GameInteraction *Caldoria::makeInteraction(const InteractionID interactionID) {
+ switch (interactionID) {
+ case kCaldoria4DInteractionID:
+ return new Caldoria4DSystem(this);
+ case kCaldoriaBombInteractionID:
+ return new CaldoriaBomb(this, _vm);
+ case kCaldoriaMessagesInteractionID:
+ return new CaldoriaMessages(this, kCaldoriaMessagesNotificationID, _vm);
+ case kCaldoriaMirrorInteractionID:
+ return new CaldoriaMirror(this);
+ }
+
+ return 0;
+}
+
+void Caldoria::newInteraction(const InteractionID interactionID) {
+ Neighborhood::newInteraction(interactionID);
+
+ if (!_currentInteraction) {
+ if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) {
+ _privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, false);
+ setCurrentActivation(kActivate4DClosed);
+ startExtraSequence(k4DEnvironClose, kExtraCompletedFlag, kFilterNoInput);
+ } else if (GameState.getCaldoriaBombDisarmed()) {
+ turnLeft();
+ }
+ }
+}
+
+// Only called when trying to pick up an item and the player can't (because
+// the inventory is too full or because the player lets go of the item before
+// dropping it into the inventory).
+Hotspot *Caldoria::getItemScreenSpot(Item *item, DisplayElement *element) {
+ HotSpotID destSpotID = kNoHotSpotID;
+
+ switch (item->getObjectID()) {
+ case kKeyCard:
+ destSpotID = kCaldoriaKeyCardSpotID;
+ break;
+ case kOrangeJuiceGlassEmpty:
+ case kOrangeJuiceGlassFull:
+ destSpotID = kCaldoriaOrangeJuiceSpotID;
+ break;
+ }
+
+ if (destSpotID == kNoHotSpotID)
+ return Neighborhood::getItemScreenSpot(item, element);
+
+ return _vm->getAllHotspots().findHotspotByID(destSpotID);
+}
+
+void Caldoria::pickedUpItem(Item *item) {
+ switch (item->getObjectID()) {
+ case kKeyCard:
+ GameState.setScoringGotKeyCard(true);
+ break;
+ case kOrangeJuiceGlassFull:
+ setCurrentActivation(kActivateReplicatorReady);
+ requestSpotSound(kCaldoriaDrinkOJIn, kCaldoriaDrinkOJOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case kStunGun:
+ GameState.setCaldoriaGunAimed(false);
+ break;
+ }
+}
+
+void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
+ switch (item->getObjectID()) {
+ case kKeyCard:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ if (dropSpot->getObjectID() == kCaldoriaGTCardDropSpotID)
+ startExtraSequence(kCaGTCardSwipe, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kOrangeJuiceGlassEmpty:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ if (dropSpot->getObjectID() == kCaldoriaOrangeJuiceDropSpotID) {
+ GameState.setCaldoriaMadeOJ(false);
+ startExtraSequence(kDisposeOrangeJuice, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kCardBomb:
+ GameState.setCaldoriaDoorBombed(true);
+ setCurrentActivation(kActivateHotSpotAlways);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ // Long enough for AI hints...?
+ _utilityFuse.primeFuse(kCardBombCountDownTime);
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::doorBombTimerExpired));
+ _utilityFuse.lightFuse();
+ GameState.setCaldoriaFuseTimeLimit(kCardBombCountDownTime);
+ loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop);
+ GameState.setScoringUsedCardBomb(true);
+ break;
+ case kStunGun:
+ GameState.setCaldoriaGunAimed(true);
+ GameState.setCaldoriaSinclairShot(true);
+ _gunSprite = item->getDragSprite(0);
+ _gunSprite->setCurrentFrameIndex(1);
+ _gunSprite->setDisplayOrder(kDragSpriteOrder);
+ _gunSprite->moveElementTo(kCaldoriaGunSpriteLeft, kCaldoriaGunSpriteTop);
+ _gunSprite->startDisplaying();
+ _gunSprite->show();
+ break;
+ default:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ }
+}
+
+void Caldoria::takeElevator(uint startFloor, uint endFloor) {
+ _croppedMovie.stop();
+ _croppedMovie.setSegment(0, _croppedMovie.getDuration());
+
+ switch (startFloor) {
+ case 1:
+ switch (endFloor) {
+ case 1:
+ // Do nothing.
+ break;
+ case 2:
+ _croppedMovie.setTime(k1To2Time);
+ requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 3:
+ _croppedMovie.setTime(k1To3Time);
+ requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 4:
+ _croppedMovie.setSegment(k1To4Start, k1To4Stop);
+ _croppedMovie.setTime(k1To4Start);
+ startExtraSequence(kCaldoriaGroundToFourth, kExtraCompletedFlag, false);
+ _croppedMovie.start();
+ break;
+ case 5:
+ _croppedMovie.setSegment(k1To5Start, k1To5Stop);
+ _croppedMovie.setTime(k1To5Start);
+ startExtraSequence(kCaldoriaGroundToRoof, kExtraCompletedFlag, false);
+ _croppedMovie.start();
+ break;
+ }
+ break;
+ case 4:
+ switch (endFloor) {
+ case 1:
+ _croppedMovie.setSegment(k4To1Start, k4To1Stop);
+ _croppedMovie.setTime(k4To1Start);
+ startExtraSequence(kCaldoriaFourthToGround, kExtraCompletedFlag, false);
+ _croppedMovie.start();
+ break;
+ case 2:
+ _croppedMovie.setTime(k4To2Time);
+ requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 3:
+ _croppedMovie.setTime(k4To3Time);
+ requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 4:
+ // Do nothing.
+ break;
+ case 5:
+ _croppedMovie.setSegment(k4To5Start, k4To5Stop);
+ _croppedMovie.setTime(k4To5Start);
+ startExtraSequence(kCaldoriaFourthToRoof, kExtraCompletedFlag, false);
+ _croppedMovie.start();
+ break;
+ }
+ break;
+ case 5:
+ switch (endFloor) {
+ case 1:
+ _croppedMovie.setSegment(k5To1Start, k5To1Stop);
+ _croppedMovie.setTime(k5To1Start);
+ startExtraSequence(kCaldoriaRoofToGround, kExtraCompletedFlag, false);
+ _croppedMovie.start();
+ break;
+ case 2:
+ _croppedMovie.setTime(k5To2Time);
+ requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 3:
+ _croppedMovie.setTime(k5To3Time);
+ requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 4:
+ _croppedMovie.setSegment(k5To4Start, k5To4Stop);
+ _croppedMovie.setTime(k5To4Start);
+ startExtraSequence(kCaldoriaRoofToFourth, kExtraCompletedFlag, false);
+ _croppedMovie.start();
+ break;
+ case 5:
+ // Do nothing.
+ break;
+ }
+ break;
+ };
+}
+
+void Caldoria::updateElevatorMovie() {
+ TimeValue time = 0xffffffff;
+
+ if (GameState.getCurrentDirection() == kNorth) {
+ switch (GameState.getCurrentRoom()) {
+ case kCaldoria27:
+ time = k4FloorTime;
+ break;
+ case kCaldoria28:
+ time = k1FloorTime;
+ break;
+ case kCaldoria45:
+ time = k5FloorTime;
+ break;
+ }
+ }
+
+ _croppedMovie.stop();
+
+ if (time == 0xffffffff) {
+ _croppedMovie.hide();
+ } else {
+ _croppedMovie.stop();
+ _croppedMovie.setSegment(0, _croppedMovie.getDuration());
+ _croppedMovie.setTime(time);
+ _croppedMovie.redrawMovieWorld();
+ _croppedMovie.show();
+
+ // *** Why do I need this?
+ // clone2727: "don't ask me!"
+ _navMovie.redrawMovieWorld();
+ }
+}
+
+void Caldoria::openElevatorMovie() {
+ if (!_croppedMovie.isSurfaceValid())
+ openCroppedMovie("Images/Caldoria/Caldoria Elevator.movie", kCaldoriaElevatorLeft, kCaldoriaElevatorTop);
+
+ updateElevatorMovie();
+}
+
+void Caldoria::emptyOJGlass() {
+ GameState.setTakenItemID(kOrangeJuiceGlassFull, false);
+ GameState.setTakenItemID(kOrangeJuiceGlassEmpty, true);
+ _vm->removeItemFromInventory((InventoryItem *)_vm->getAllItems().findItemByID(kOrangeJuiceGlassFull));
+ _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kOrangeJuiceGlassEmpty));
+}
+
+void Caldoria::doorBombTimerExpired() {
+ closeCroppedMovie();
+
+ if (GameState.getShieldOn()) {
+ _vm->getCurrentBiochip()->setItemState(kShieldCardBomb);
+ setCurrentAlternate(kAltCaldoriaRoofDoorBlown);
+ startExtraSequence(kCa48NorthExplosion, kExtraCompletedFlag, kFilterNoInput);
+ GameState.setScoringShieldedCardBomb(true);
+ GameState.setCaldoriaDoorBombed(false);
+ GameState.setCaldoriaRoofDoorOpen(true);
+ } else {
+ playDeathExtra(kCa48NorthExplosionDeath, kDeathCardBomb);
+ }
+}
+
+void Caldoria::sinclairTimerExpired() {
+ _privateFlags.setFlag(kCaldoriaPrivateSinclairTimerExpiredFlag, true);
+ checkSinclairShootsOS();
+}
+
+void Caldoria::checkSinclairShootsOS() {
+ if (_privateFlags.getFlag(kCaldoriaPrivateSinclairTimerExpiredFlag))
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kCaldoria49, kNorth):
+ case MakeRoomView(kCaldoria49, kSouth):
+ case MakeRoomView(kCaldoria49, kEast):
+ case MakeRoomView(kCaldoria49, kWest):
+ case MakeRoomView(kCaldoria50, kSouth):
+ case MakeRoomView(kCaldoria50, kEast):
+ case MakeRoomView(kCaldoria50, kWest):
+ case MakeRoomView(kCaldoria51, kNorth):
+ case MakeRoomView(kCaldoria51, kSouth):
+ case MakeRoomView(kCaldoria51, kWest):
+ case MakeRoomView(kCaldoria52, kNorth):
+ case MakeRoomView(kCaldoria52, kSouth):
+ case MakeRoomView(kCaldoria52, kWest):
+ case MakeRoomView(kCaldoria53, kNorth):
+ case MakeRoomView(kCaldoria53, kSouth):
+ case MakeRoomView(kCaldoria53, kWest):
+ case MakeRoomView(kCaldoria54, kNorth):
+ case MakeRoomView(kCaldoria54, kEast):
+ case MakeRoomView(kCaldoria54, kWest):
+ playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut);
+ playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut);
+ die(kDeathSinclairShotDelegate);
+ break;
+ }
+}
+
+void Caldoria::checkInterruptSinclair() {
+ if (GameState.getCaldoriaSinclairShot()) {
+ _navMovie.stop();
+ _neighborhoodNotification.setNotificationFlags(kExtraCompletedFlag, kExtraCompletedFlag);
+ g_AIArea->unlockAI();
+ } else {
+ uint32 currentTime = _navMovie.getTime();
+
+ ExtraTable::Entry entry;
+ getExtraEntry(kCa53EastZoomToSinclair, entry);
+
+ if (currentTime < entry.movieStart + kSinclairInterruptionTime2)
+ _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime2,
+ _navMovie.getScale());
+ else if (currentTime < entry.movieStart + kSinclairInterruptionTime3)
+ _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime3,
+ _navMovie.getScale());
+ else if (currentTime < entry.movieStart + kSinclairInterruptionTime4)
+ _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime4,
+ _navMovie.getScale());
+ }
+}
+
+Common::String Caldoria::getBriefingMovie() {
+ Common::String movieName = Neighborhood::getBriefingMovie();
+
+ if (movieName.empty()) {
+ if (GameState.allTimeZonesFinished())
+ return "Images/AI/Caldoria/XA02";
+
+ return "Images/AI/Caldoria/XA01";
+ }
+
+ return movieName;
+}
+
+Common::String Caldoria::getEnvScanMovie() {
+ Common::String movieName = Neighborhood::getEnvScanMovie();
+
+ if (movieName.empty()) {
+ RoomID room = GameState.getCurrentRoom();
+
+ if (room >= kCaldoria00 && room <= kCaldoria14) {
+ // Inside apartment.
+ if (GameState.getCaldoriaDoneHygiene())
+ return "Images/AI/Caldoria/XAE2";
+
+ return "Images/AI/Caldoria/XAE1";
+ } else if (room >= kCaldoria15 && room <= kCaldoria48) {
+ // Wandering the halls...
+ return "Images/AI/Caldoria/XAE3";
+ } else {
+ // Must be the roof.
+ return "Images/AI/Caldoria/XAEH2";
+ }
+ }
+
+ return movieName;
+}
+
+uint Caldoria::getNumHints() {
+ uint numHints = Neighborhood::getNumHints();
+
+ if (numHints == 0) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kCaldoria44, kEast):
+ if (!GameState.isTakenItemID(kKeyCard) && GameState.getOpenDoorRoom() == kNoRoomID)
+ numHints = 1;
+ break;
+ case MakeRoomView(kCaldoria48, kNorth):
+ if (!GameState.getCaldoriaRoofDoorOpen()) {
+ if (_croppedMovie.isRunning()) // Bomb must be looping.
+ numHints = 3;
+ else if (GameState.isTakenItemID(kCardBomb))
+ numHints = 1;
+ }
+ break;
+ case MakeRoomView(kCaldoria49, kEast):
+ case MakeRoomView(kCaldoria54, kEast):
+ numHints = 1;
+ break;
+ case MakeRoomView(kCaldoria49, kNorth):
+ numHints = 1;
+ break;
+ }
+ }
+
+ return numHints;
+}
+
+Common::String Caldoria::getHintMovie(uint hintNum) {
+ Common::String movieName = Neighborhood::getHintMovie(hintNum);
+
+ if (movieName.empty()) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kCaldoria44, kEast):
+ return "Images/AI/Caldoria/X42WH2";
+ case MakeRoomView(kCaldoria48, kNorth):
+ if (_croppedMovie.isRunning()) { // Bomb must be looping.
+ if (hintNum == 1)
+ return "Images/AI/Caldoria/X48ND1";
+ else if (hintNum == 2)
+ return "Images/AI/Caldoria/X48ND2";
+ else if (GameState.isTakenItemID(kShieldBiochip))
+ return "Images/AI/Caldoria/X48ND3";
+
+ // *** Doesn't work yet, need global movies.
+ break;
+ }
+
+ return "Images/AI/Globals/XGLOB1A";
+ case MakeRoomView(kCaldoria49, kEast):
+ case MakeRoomView(kCaldoria54, kEast):
+ return "Images/AI/Caldoria/X49E";
+ case MakeRoomView(kCaldoria49, kNorth):
+ return "Images/AI/Caldoria/X49NB2";
+ }
+ }
+
+ return movieName;
+}
+
+void Caldoria::updateCursor(const Common::Point where, const Hotspot *cursorSpot) {
+ if (cursorSpot) {
+ switch (cursorSpot->getObjectID()) {
+ case kCa4DEnvironCloseSpotID:
+ _vm->_cursor->setCurrentFrameIndex(2);
+ return;
+ case kCaldoriaKioskSpotID:
+ _vm->_cursor->setCurrentFrameIndex(3);
+ return;
+ }
+ }
+
+ Neighborhood::updateCursor(where, cursorSpot);
+}
+
+Common::String Caldoria::getNavMovieName() {
+ return "Images/Caldoria/Caldoria.movie";
+}
+
+Common::String Caldoria::getSoundSpotsName() {
+ return "Sounds/Caldoria/Caldoria Spots";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.h b/engines/pegasus/neighborhood/caldoria/caldoria.h
new file mode 100644
index 0000000000..3d6a155170
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoria.h
@@ -0,0 +1,523 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H
+#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H
+
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+static const TimeScale kCaldoriaMovieScale = 600;
+static const TimeScale kCaldoriaFramesPerSecond = 15;
+static const TimeScale kCaldoriaFrameDuration = 40;
+
+// Alternate IDs.
+
+static const AlternateID kAltCaldoriaNormal = 0;
+static const AlternateID kAltCaldoriaRoofDoorBlown = 2;
+static const AlternateID kAltCaldoriaSinclairDown = 3;
+
+// Room IDs.
+
+static const RoomID kCaldoria00 = 1;
+static const RoomID kCaldoria01 = 2;
+static const RoomID kCaldoria02 = 3;
+static const RoomID kCaldoria03 = 4;
+static const RoomID kCaldoria04 = 5;
+static const RoomID kCaldoria05 = 6;
+static const RoomID kCaldoria06 = 7;
+static const RoomID kCaldoria07 = 8;
+static const RoomID kCaldoria08 = 9;
+static const RoomID kCaldoria09 = 10;
+static const RoomID kCaldoria10 = 11;
+static const RoomID kCaldoriaToilet = 12;
+static const RoomID kCaldoria11 = 13;
+static const RoomID kCaldoria12 = 14;
+static const RoomID kCaldoriaVidPhone = 15;
+static const RoomID kCaldoriaReplicator = 16;
+static const RoomID kCaldoriaDrawers = 17;
+static const RoomID kCaldoria13 = 18;
+static const RoomID kCaldoria14 = 19;
+static const RoomID kCaldoria15 = 20;
+static const RoomID kCaldoria16 = 21;
+static const RoomID kCaldoria17 = 22;
+static const RoomID kCaldoria18 = 23;
+static const RoomID kCaldoria19 = 24;
+static const RoomID kCaldoria20 = 25;
+static const RoomID kCaldoria21 = 26;
+static const RoomID kCaldoria22 = 27;
+static const RoomID kCaldoria23 = 28;
+static const RoomID kCaldoria24 = 29;
+static const RoomID kCaldoria25 = 30;
+static const RoomID kCaldoria26 = 31;
+static const RoomID kCaldoria27 = 32;
+static const RoomID kCaldoria28 = 33;
+static const RoomID kCaldoria29 = 34;
+static const RoomID kCaldoria30 = 35;
+static const RoomID kCaldoria31 = 36;
+static const RoomID kCaldoria32 = 37;
+static const RoomID kCaldoria33 = 38;
+static const RoomID kCaldoria34 = 39;
+static const RoomID kCaldoria35 = 40;
+static const RoomID kCaldoria36 = 41;
+static const RoomID kCaldoria37 = 42;
+static const RoomID kCaldoria38 = 43;
+static const RoomID kCaldoria39 = 44;
+static const RoomID kCaldoria40 = 45;
+static const RoomID kCaldoria41 = 46;
+static const RoomID kCaldoriaBinoculars = 47;
+static const RoomID kCaldoria42 = 48;
+static const RoomID kCaldoriaKiosk = 49;
+static const RoomID kCaldoria44 = 50;
+static const RoomID kCaldoria45 = 51;
+static const RoomID kCaldoria46 = 52;
+static const RoomID kCaldoria47 = 53;
+static const RoomID kCaldoria48 = 54;
+static const RoomID kCaldoria49 = 55;
+static const RoomID kCaldoria50 = 56;
+static const RoomID kCaldoria51 = 57;
+static const RoomID kCaldoria52 = 58;
+static const RoomID kCaldoria53 = 59;
+static const RoomID kCaldoria54 = 60;
+static const RoomID kCaldoria55 = 61;
+static const RoomID kCaldoria56 = 62;
+static const RoomID kCaldoriaDeathRoom = 0;
+
+// Hot Spot Activation IDs.
+
+static const HotSpotActivationID kActivate4DClosed = 1;
+static const HotSpotActivationID kActivate4DOpen = 2;
+static const HotSpotActivationID kActivateMirrorReady = 3;
+static const HotSpotActivationID kActivateStylistReady = 4;
+static const HotSpotActivationID kActivateReplicatorReady = 5;
+static const HotSpotActivationID kActivateOJOnThePad = 6;
+static const HotSpotActivationID kActivateDrawersClosed = 7;
+static const HotSpotActivationID kActivateRightOpen = 8;
+static const HotSpotActivationID kActivateLeftOpen = 9;
+static const HotSpotActivationID kActivateFocusedOnShip = 10;
+static const HotSpotActivationID kActivateNotFocusedOnShip = 11;
+static const HotSpotActivationID kActivateReadyForCard = 12;
+static const HotSpotActivationID kActivateReadyToTransport = 13;
+static const HotSpotActivationID kActivateRoofSlotEmpty = 14;
+static const HotSpotActivationID kActivateZoomedOnSinclair = 15;
+
+// Hot Spot IDs.
+
+static const HotSpotID kCa4DEnvironOpenSpotID = 5000;
+static const HotSpotID kCa4DEnvironCloseSpotID = 5001;
+static const HotSpotID kCa4DVisualSpotID = 5002;
+static const HotSpotID kCa4DAudioSpotID = 5003;
+static const HotSpotID kCa4DChoice1SpotID = 5004;
+static const HotSpotID kCa4DChoice2SpotID = 5005;
+static const HotSpotID kCa4DChoice3SpotID = 5006;
+static const HotSpotID kCa4DChoice4SpotID = 5007;
+static const HotSpotID kCaBathroomMirrorSpotID = 5008;
+static const HotSpotID kCaHairStyle1SpotID = 5009;
+static const HotSpotID kCaHairStyle2SpotID = 5010;
+static const HotSpotID kCaHairStyle3SpotID = 5011;
+static const HotSpotID kCaShowerSpotID = 5012;
+static const HotSpotID kCaBathroomToiletSpotID = 5013;
+static const HotSpotID kCaldoriaVidPhoneSpotID = 5014;
+static const HotSpotID kCaldoriaReplicatorSpotID = 5015;
+static const HotSpotID kCaldoriaDrawersSpotID = 5016;
+static const HotSpotID kCaldoriaVidPhoneOutSpotID = 5017;
+static const HotSpotID kCaBedroomVidPhoneActivationSpotID = 5018;
+static const HotSpotID kCaldoriaReplicatorOutSpotID = 5019;
+static const HotSpotID kCaldoriaMakeOJSpotID = 5020;
+static const HotSpotID kCaldoriaMakeStickyBunsSpotID = 5021;
+static const HotSpotID kCaldoriaOrangeJuiceSpotID = 5022;
+static const HotSpotID kCaldoriaOrangeJuiceDropSpotID = 5023;
+static const HotSpotID kCaldoriaDrawersOutSpotID = 5024;
+static const HotSpotID kCaldoriaLeftDrawerOpenSpotID = 5025;
+static const HotSpotID kCaldoriaRightDrawerOpenSpotID = 5026;
+static const HotSpotID kCaldoriaKeyCardSpotID = 5027;
+static const HotSpotID kCaldoriaLeftDrawerCloseSpotID = 5028;
+static const HotSpotID kCaldoriaRightDrawerWithKeysCloseSpotID = 5029;
+static const HotSpotID kCaldoriaRightDrawerNoKeysCloseSpotID = 5030;
+static const HotSpotID kCaldoriaFourthFloorElevatorSpotID = 5031;
+static const HotSpotID kCaldoria20DoorbellSpotID = 5032;
+static const HotSpotID kCaldoria21DoorbellSpotID = 5033;
+static const HotSpotID kCaldoria26DoorbellSpotID = 5034;
+static const HotSpotID kCaldoriaFourthFloorElevator1 = 5035;
+static const HotSpotID kCaldoriaFourthFloorElevator2 = 5036;
+static const HotSpotID kCaldoriaFourthFloorElevator3 = 5037;
+static const HotSpotID kCaldoriaFourthFloorElevator4 = 5038;
+static const HotSpotID kCaldoriaFourthFloorElevator5 = 5039;
+static const HotSpotID kCaldoriaGroundElevator1 = 5040;
+static const HotSpotID kCaldoriaGroundElevator2 = 5041;
+static const HotSpotID kCaldoriaGroundElevator3 = 5042;
+static const HotSpotID kCaldoriaGroundElevator4 = 5043;
+static const HotSpotID kCaldoriaGroundElevator5 = 5044;
+static const HotSpotID kCaldoria29DoorbellSpotID = 5045;
+static const HotSpotID kCaldoria34DoorbellSpotID = 5046;
+static const HotSpotID kCaldoria35DoorbellSpotID = 5047;
+static const HotSpotID kCaldoriaGroundElevatorSpotID = 5048;
+static const HotSpotID kCaldoriaBinocularZoomInSpotID = 5049;
+static const HotSpotID kCaldoriaBinocularsOutSpotID = 5050;
+static const HotSpotID kCaldoriaZoomInOnShipSpotID = 5051;
+static const HotSpotID kCaldoriaKioskSpotID = 5052;
+static const HotSpotID kCaldoriaKioskOutSpotID = 5053;
+static const HotSpotID kCaldoriaKioskInfoSpotID = 5054;
+static const HotSpotID kCaldoriaGTCardDropSpotID = 5055;
+static const HotSpotID kCaldoriaGTTokyoSpotID = 5056;
+static const HotSpotID kCaldoriaGTTSASpotID = 5057;
+static const HotSpotID kCaldoriaGTBeachSpotID = 5058;
+static const HotSpotID kCaldoriaGTOtherSpotID = 5059;
+static const HotSpotID kCaldoriaRoofElevator1 = 5060;
+static const HotSpotID kCaldoriaRoofElevator2 = 5061;
+static const HotSpotID kCaldoriaRoofElevator3 = 5062;
+static const HotSpotID kCaldoriaRoofElevator4 = 5063;
+static const HotSpotID kCaldoriaRoofElevator5 = 5064;
+static const HotSpotID kCaldoriaRoofElevatorSpotID = 5065;
+static const HotSpotID kCaldoriaRoofDoorSpotID = 5066;
+static const HotSpotID kCaldoriaRoofCardDropSpotID = 5067;
+static const HotSpotID kCaldoria53EastSinclairTargetSpotID = 5068;
+
+// Extra sequence IDs.
+
+static const ExtraID kCaldoriaWakeUpView1 = 0;
+static const ExtraID kCaldoria00WakeUp1 = 1;
+static const ExtraID kCaldoria00WakeUp2 = 2;
+static const ExtraID kCaldoria00SitDown = 3;
+static const ExtraID k4DEnvironOpenToINN = 4;
+static const ExtraID k4DINNInterruption = 5;
+static const ExtraID k4DINNIntro = 6;
+static const ExtraID k4DINNMarkJohnson = 7;
+static const ExtraID k4DINNMeganLove = 8;
+static const ExtraID k4DINNFadeOut = 9;
+static const ExtraID k4DEnvironOpenFromINN = 10;
+static const ExtraID k4DEnvironOpen = 11;
+static const ExtraID k4DEnvironOpenView = 12;
+static const ExtraID k4DEnvironClose = 13;
+static const ExtraID k4DIslandLoop = 14;
+static const ExtraID k4DDesertLoop = 15;
+static const ExtraID k4DMountainLoop = 16;
+static const ExtraID k4DIsland1ToIsland0 = 17;
+static const ExtraID k4DIsland2ToIsland0 = 18;
+static const ExtraID k4DIsland0ToDesert0 = 19;
+static const ExtraID k4DIsland1ToDesert0 = 20;
+static const ExtraID k4DIsland2ToDesert0 = 21;
+static const ExtraID k4DIsland0ToMountain0 = 22;
+static const ExtraID k4DIsland1ToMountain0 = 23;
+static const ExtraID k4DIsland2ToMountain0 = 24;
+static const ExtraID k4DDesert0ToIsland0 = 25;
+static const ExtraID k4DDesert1ToIsland0 = 26;
+static const ExtraID k4DDesert2ToIsland0 = 27;
+static const ExtraID k4DDesert0ToMountain0 = 28;
+static const ExtraID k4DDesert1ToMountain0 = 29;
+static const ExtraID k4DDesert2ToMountain0 = 30;
+static const ExtraID k4DMountain0ToIsland0 = 31;
+static const ExtraID k4DMountain1ToIsland0 = 32;
+static const ExtraID k4DMountain2ToIsland0 = 33;
+static const ExtraID k4DMountain0ToDesert0 = 34;
+static const ExtraID k4DMountain1ToDesert0 = 35;
+static const ExtraID k4DMountain2ToDesert0 = 36;
+static const ExtraID kCaBathroomGreeting = 37;
+static const ExtraID kCaBathroomBodyFat = 38;
+static const ExtraID kCaBathroomStylistIntro = 39;
+static const ExtraID kCaBathroomRetrothrash = 40;
+static const ExtraID kCaBathroomRetrothrashReturn = 41;
+static const ExtraID kCaBathroomGeoWave = 42;
+static const ExtraID kCaBathroomGeoWaveReturn = 43;
+static const ExtraID kCaBathroomAgencyStandard = 44;
+static const ExtraID kCaldoriaShowerTitle = 45;
+static const ExtraID kCaldoriaShowerButton = 46;
+static const ExtraID kCaldoriaShowerDown = 47;
+static const ExtraID kCaldoriaShowerUp = 48;
+static const ExtraID kCaBedroomVidPhone = 49;
+static const ExtraID kCaBedroomMessage1 = 50;
+static const ExtraID kCaBedroomMessage2 = 51;
+static const ExtraID kCreateOrangeJuice = 52;
+static const ExtraID kDisposeOrangeJuice = 53;
+static const ExtraID kReplicatorNorthViewWithOJ = 54;
+static const ExtraID kLeftDrawerOpen = 55;
+static const ExtraID kLeftDrawerClose = 56;
+static const ExtraID kRightDrawerOpenWithKeys = 57;
+static const ExtraID kRightDrawerCloseWithKeys = 58;
+static const ExtraID kRightDrawerOpenNoKeys = 59;
+static const ExtraID kRightDrawerCloseNoKeys = 60;
+static const ExtraID kRightDrawerOpenViewWithKeys = 61;
+static const ExtraID kRightDrawerOpenViewNoKeys = 62;
+static const ExtraID kCaldoria16ElevatorUp = 63;
+static const ExtraID kCaldoria16ElevatorDown = 64;
+static const ExtraID kCaldoria16SouthViewWithElevator = 65;
+static const ExtraID kCaldoria20Doorbell = 66;
+static const ExtraID kCaldoria21Doorbell = 67;
+static const ExtraID kCaldoria26Doorbell = 68;
+static const ExtraID kCaldoriaFourthToGround = 69;
+static const ExtraID kCaldoriaRoofToFourth = 70;
+static const ExtraID kCaldoriaRoofToGround = 71;
+static const ExtraID kCaldoriaGroundToFourth = 72;
+static const ExtraID kCaldoriaGroundToRoof = 73;
+static const ExtraID kCaldoriaFourthToRoof = 74;
+static const ExtraID kCaldoria29Doorbell = 75;
+static const ExtraID kCaldoria34Doorbell = 76;
+static const ExtraID kCaldoria35Doorbell = 77;
+static const ExtraID kBinocularsZoomInOnShip = 78;
+static const ExtraID kCaldoriaKioskVideo = 79;
+static const ExtraID kCaldoriaTransporterArrowLoop = 80;
+static const ExtraID kArriveAtCaldoriaFromTSA = 81;
+static const ExtraID kCaGTOtherChoice = 82;
+static const ExtraID kCaGTCardSwipe = 83;
+static const ExtraID kCaGTSelectTSA = 84;
+static const ExtraID kCaGTFryTheFly = 85;
+static const ExtraID kCaGTGoToTSA = 86;
+static const ExtraID kCaGTSelectBeach = 87;
+static const ExtraID kCaGTGoToBeach = 88;
+static const ExtraID kCaGTArriveAtBeach = 89;
+static const ExtraID kCaGTSelectTokyo = 90;
+static const ExtraID kCaGTGoToTokyo = 91;
+static const ExtraID kCaGTArriveAtTokyo = 92;
+static const ExtraID kCa48NorthRooftopClosed = 93;
+static const ExtraID kCa48NorthExplosion = 94;
+static const ExtraID kCa48NorthExplosionDeath = 95;
+static const ExtraID kCa49NorthVoiceAnalysis = 96;
+static const ExtraID kCa50SinclairShoots = 97;
+static const ExtraID kCa53EastZoomToSinclair = 98;
+static const ExtraID kCa53EastDeath2 = 99;
+static const ExtraID kCa53EastShootSinclair = 100;
+static const ExtraID kCa53EastZoomOutFromSinclair = 101;
+static const ExtraID kCa54SouthDeath = 102;
+static const ExtraID kCaldoria56BombStage1 = 103;
+static const ExtraID kCaldoria56BombStage2 = 104;
+static const ExtraID kCaldoria56BombStage3 = 105;
+static const ExtraID kCaldoria56BombStage4 = 106;
+static const ExtraID kCaldoria56BombStage5 = 107;
+static const ExtraID kCaldoria56BombStage6 = 108;
+static const ExtraID kCaldoria56BombStage7 = 109;
+static const ExtraID kCaldoria56BombExplodes = 110;
+
+// Caldoria interactions.
+
+static const InteractionID kCaldoria4DInteractionID = 0;
+static const InteractionID kCaldoriaBombInteractionID = 1;
+static const InteractionID kCaldoriaMessagesInteractionID = 2;
+static const InteractionID kCaldoriaMirrorInteractionID = 3;
+
+// Caldoria:
+
+static const DisplayOrder kVidPhoneOrder = kMonitorLayer;
+static const DisplayOrder k4DSpritesOrder = kMonitorLayer;
+static const DisplayOrder kCaldoriaMessagesOrder = kMonitorLayer;
+static const DisplayOrder kCaldoriaElevatorOrder = kMonitorLayer;
+static const DisplayOrder kCaldoriaA05LightLoopOrder = kMonitorLayer;
+static const DisplayOrder kCaldoriaA07LightLoopOrder = kMonitorLayer;
+static const DisplayOrder kCaldoriaBombGridOrder = kMonitorLayer;
+static const DisplayOrder kCaldoriaBombTimerOrder = kCaldoriaBombGridOrder + 1;
+
+/////////////////////////////////////////////
+//
+// Caldoria
+
+static const CoordType kCaldoriaVidPhoneLeft = kNavAreaLeft + 105;
+static const CoordType kCaldoriaVidPhoneTop = kNavAreaTop + 28;
+
+static const CoordType kCaldoria4DSpritesLeft = kNavAreaLeft + 10;
+static const CoordType kCaldoria4DSpritesTop = kNavAreaTop + 142;
+
+static const CoordType kCaldoriaMessageLeft = kNavAreaLeft + 202;
+static const CoordType kCaldoriaMessageTop = kNavAreaTop + 26;
+
+static const CoordType kCaldoriaElevatorLeft = kNavAreaLeft + 407;
+static const CoordType kCaldoriaElevatorTop = kNavAreaTop + 138;
+
+static const CoordType kCaldoriaA05LightLoopLeft = kNavAreaLeft + 213;
+static const CoordType kCaldoriaA05LightLoopTop = kNavAreaTop + 215;
+
+static const CoordType kCaldoriaA07LightLoopLeft = kNavAreaLeft + 414;
+static const CoordType kCaldoriaA07LightLoopTop = kNavAreaTop + 215;
+
+static const CoordType kCaldoriaGunSpriteLeft = kNavAreaLeft + 276;
+static const CoordType kCaldoriaGunSpriteTop = kNavAreaTop + 115;
+
+static const CoordType kCaldoria11MessageLoopLeft = kNavAreaLeft + 135;
+static const CoordType kCaldoria11MessageLoopTop = kNavAreaTop + 214;
+
+static const CoordType kCaldoria12MessageLoopLeft = kNavAreaLeft + 209;
+static const CoordType kCaldoria12MessageLoopTop = kNavAreaTop + 170;
+
+static const CoordType kCaldoria13MessageLoopLeft = kNavAreaLeft + 480;
+static const CoordType kCaldoria13MessageLoopTop = kNavAreaTop + 191;
+
+static const CoordType kCaldoria14MessageLoopLeft = kNavAreaLeft + 248;
+static const CoordType kCaldoria14MessageLoopTop = kNavAreaTop + 191;
+
+static const CoordType kCaldoria48CardBombLoopLeft = kNavAreaLeft + 337;
+static const CoordType kCaldoria48CardBombLoopTop = kNavAreaTop + 205;
+
+static const CoordType kCaldoriaBombGridLeft = kNavAreaLeft + 290;
+static const CoordType kCaldoriaBombGridTop = kNavAreaTop + 58;
+
+static const CoordType kCaldoriaBombTimerLeft = kNavAreaLeft + 58;
+static const CoordType kCaldoriaBombTimerTop = kNavAreaTop + 204;
+
+// Caldoria display IDs.
+
+static const DisplayElementID kCaldoriaVidPhoneID = kNeighborhoodDisplayID;
+static const DisplayElementID kCaldoria4DSpritesID = kCaldoriaVidPhoneID + 1;
+static const DisplayElementID kCaldoriaMessagesID = kCaldoria4DSpritesID + 1;
+static const DisplayElementID kCaldoriaUtilityID = kCaldoriaMessagesID + 1;
+static const DisplayElementID kCaldoriaBombGridID = kCaldoriaUtilityID + 1;
+static const DisplayElementID kCaldoriaBombTimerID = kCaldoriaBombGridID + 1;
+
+static const TimeValue kCaldoria4DBlankChoiceIn = 29730;
+static const TimeValue kCaldoria4DBlankChoiceOut = 33910;
+
+class Caldoria;
+
+class SinclairCallBack : public TimeBaseCallBack {
+public:
+ SinclairCallBack(Caldoria *);
+ ~SinclairCallBack() {}
+
+protected:
+ virtual void callBack();
+
+ Caldoria *_caldoria;
+};
+
+class Caldoria : public Neighborhood {
+friend class SinclairCallBack;
+
+public:
+ Caldoria(InputHandler *, PegasusEngine *);
+ virtual ~Caldoria();
+
+ virtual uint16 getDateResID() const;
+
+ void pickedUpItem(Item *);
+
+ virtual GameInteraction *makeInteraction(const InteractionID);
+
+ virtual Common::String getBriefingMovie();
+ virtual Common::String getEnvScanMovie();
+ virtual uint getNumHints();
+ virtual Common::String getHintMovie(uint);
+ void loadAmbientLoops();
+ bool wantsCursor();
+ void flushGameState();
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+protected:
+ enum {
+ kCaldoriaPrivate4DSystemOpenFlag,
+ kCaloriaPrivateLeftDrawerOpenFlag,
+ kCaldoriaPrivateRightDrawerOpenFlag,
+ kCaldoriaPrivateReadyToShootFlag,
+ kCaldoriaPrivateZoomingToBombFlag,
+ kCaldoriaPrivateCanOpenElevatorDoorFlag,
+ kCaldoriaPrivateSinclairTimerExpiredFlag,
+ kCaldoriaPrivateSeen13CarFlag,
+ kCaldoriaPrivateSeen14CarFlag,
+ kCaldoriaPrivateSeen18CarFlag,
+ kCaldoriaPrivateSeen23CarFlag,
+ kCaldoriaPrivateSeen33CarFlag,
+ kCaldoriaPrivateSeen36CarFlag,
+ kCaldoriaPrivateSeen41NorthCarFlag,
+ kCaldoriaPrivateSeen41EastCarFlag,
+ kCaldoriaPrivateSeen41WestCarFlag,
+ kNumCaldoriaPrivateFlags
+ };
+
+ void init();
+ void start();
+
+ void setUpRoofTop();
+
+ void setUpAIRules();
+ void doAIRecalibration();
+ TimeValue getViewTime(const RoomID, const DirectionConstant);
+ void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &);
+ void startSpotOnceOnly(TimeValue, TimeValue);
+ void startExitMovie(const ExitTable::Entry &);
+ void startZoomMovie(const ZoomTable::Entry &);
+ void startDoorOpenMovie(const TimeValue, const TimeValue);
+ void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant);
+ void bumpIntoWall();
+ int16 getStaticCompassAngle(const RoomID, const DirectionConstant);
+ void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &);
+ void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec &);
+ void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &);
+ void spotCompleted();
+ void arriveAt(const RoomID, const DirectionConstant);
+ void arriveAtCaldoria00();
+ void arriveAtCaldoriaToilet();
+ void arriveAtCaldoria44();
+ void arriveAtCaldoria49();
+ void arriveAtCaldoria56();
+ void arriveAtCaldoriaDeath();
+ void turnTo(const DirectionConstant);
+ void zoomTo(const Hotspot *);
+ void downButton(const Input &);
+ void receiveNotification(Notification *, const NotificationFlags);
+ InputBits getInputFilter();
+ void activateHotspots();
+ void clickInHotspot(const Input &, const Hotspot *);
+ void newInteraction(const InteractionID);
+
+ void clickOnDoorbell(const HotSpotID);
+
+ Hotspot *getItemScreenSpot(Item *, DisplayElement *);
+ void dropItemIntoRoom(Item *, Hotspot *);
+ void takeElevator(uint, uint);
+ void updateElevatorMovie();
+ void openElevatorMovie();
+ void emptyOJGlass();
+ void closeDoorOffScreen(const RoomID, const DirectionConstant);
+ void doorBombTimerExpired();
+ void sinclairTimerExpired();
+ void checkSinclairShootsOS();
+ void setUpSinclairLoops();
+ void zoomToSinclair();
+ void playEndMessage();
+ void checkInterruptSinclair();
+
+ CanOpenDoorReason canOpenDoor(DoorTable::Entry &);
+ void doorOpened();
+
+ void updateCursor(const Common::Point, const Hotspot *);
+
+ FlagsArray<uint16, kNumCaldoriaPrivateFlags> _privateFlags;
+
+ const Hotspot *_zoomOutSpot;
+
+ FuseFunction _utilityFuse;
+
+ long _sinclairLoopCount;
+ long _numSinclairLoops;
+
+ Sprite *_gunSprite;
+
+ SinclairCallBack _sinclairInterrupt;
+
+ Common::String getSoundSpotsName();
+ Common::String getNavMovieName();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp
new file mode 100644
index 0000000000..0494753661
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp
@@ -0,0 +1,370 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/neighborhood/caldoria/caldoria.h"
+#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h"
+
+namespace Pegasus {
+
+static const TimeValue kSwitchableSlop = 3 * kCaldoriaFrameDuration;
+// Two seconds - some slop
+static const TimeValue kSwitchableDuration = kCaldoriaMovieScale * 2 - kSwitchableSlop;
+// Twelve frames + some slop
+static const TimeValue kNonswitchableDuration = kCaldoriaFrameDuration * 12 + kSwitchableSlop;
+
+static const TimeValue kSwitchable1Start = 0;
+static const TimeValue kSwitchable1Stop = kSwitchable1Start + kSwitchableDuration;
+
+static const TimeValue kSwitchable2Start = kSwitchable1Stop + kNonswitchableDuration;
+static const TimeValue kSwitchable2Stop = kSwitchable2Start + kSwitchableDuration;
+
+static const TimeValue kSwitchable3Start = kSwitchable2Stop + kNonswitchableDuration;
+static const TimeValue kSwitchable3Stop = kSwitchable3Start + kSwitchableDuration;
+
+static const NotificationFlags kVidPhoneDoneFlag = 1;
+
+static const TimeValue kRockMusicLoopIn = 0;
+static const TimeValue kRockMusicLoopOut = 2088;
+
+static const TimeValue kOrchestralMusicLoopIn = 2088;
+static const TimeValue kOrchestralMusicLoopOut = 4985;
+
+static const TimeValue kRhythmsMusicLoopIn = 4985;
+static const TimeValue kRhythmsMusicLoopOut = 6824;
+
+static const TimeValue kAcousticMusicLoopIn = 6824;
+static const TimeValue kAcousticMusicLoopOut = 9387;
+
+enum {
+ k4DVideoMenu,
+ k4DAudioMenu,
+ k4DShuttingDown,
+
+ // These constants are the exact frame numbers of the sprite movie.
+ k4DRockChoice = 0,
+ k4DOrchestralChoice,
+ k4DRhythmsChoice,
+ k4DAcousticChoice,
+ k4DIslandChoice,
+ k4DDesertChoice,
+ k4DMountainChoice,
+
+ k4DFirstVideoChoice = k4DIslandChoice
+};
+
+static const ExtraID s_transitionExtras0[3][3] = {
+ { 0xffffffff, k4DIsland0ToDesert0, k4DIsland0ToMountain0 },
+ { k4DDesert0ToIsland0, 0xffffffff, k4DDesert0ToMountain0 },
+ { k4DMountain0ToIsland0, k4DMountain0ToDesert0, 0xffffffff }
+};
+
+static const ExtraID s_transitionExtras1[3][3] = {
+ { 0xffffffff, k4DIsland1ToDesert0, k4DIsland1ToMountain0 },
+ { k4DDesert1ToIsland0, 0xffffffff, k4DDesert1ToMountain0 },
+ { k4DMountain1ToIsland0, k4DMountain1ToDesert0, 0xffffffff }
+};
+
+static const ExtraID s_transitionExtras2[3][3] = {
+ { 0xffffffff, k4DIsland2ToDesert0, k4DIsland2ToMountain0 },
+ { k4DDesert2ToIsland0, 0xffffffff, k4DDesert2ToMountain0 },
+ { k4DMountain2ToIsland0, k4DMountain2ToDesert0, 0xffffffff }
+};
+
+static const ExtraID s_shutDownExtras[3][3] = {
+ { 0xffffffff, k4DIsland1ToIsland0, k4DIsland2ToIsland0 },
+ { k4DDesert0ToIsland0, k4DDesert1ToIsland0, k4DDesert2ToIsland0 },
+ { k4DMountain0ToIsland0, k4DMountain1ToIsland0, k4DMountain2ToIsland0 }
+};
+
+Caldoria4DSystem::Caldoria4DSystem(Neighborhood *owner) : GameInteraction(kCaldoria4DInteractionID, owner),
+ _4DSpritesMovie(kCaldoria4DSpritesID) {
+ g_AIArea->lockAIOut();
+}
+
+Caldoria4DSystem::~Caldoria4DSystem() {
+ g_AIArea->unlockAI();
+}
+
+void Caldoria4DSystem::openInteraction() {
+ _whichMenu = k4DVideoMenu;
+ _videoChoice = k4DIslandChoice;
+ _audioChoice = k4DRockChoice;
+ _clickedHotspotID = kNoHotSpotID;
+
+ _4DSpritesMovie.initFromMovieFile("Images/Caldoria/4D Sprites", true);
+ _4DSpritesMovie.moveElementTo(kCaldoria4DSpritesLeft, kCaldoria4DSpritesTop);
+ _4DSpritesMovie.setDisplayOrder(k4DSpritesOrder);
+ _4DSpritesMovie.startDisplaying();
+ _4DSpritesMovie.show();
+
+ _4DSpritesScale = _4DSpritesMovie.getScale();
+
+ _neighborhoodNotification = _owner->getNeighborhoodNotification();
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
+
+ startIdling();
+}
+
+void Caldoria4DSystem::loopExtra(const ExtraID extraID) {
+ ExtraTable::Entry extraEntry;
+
+ _owner->getExtraEntry(extraID, extraEntry);
+ _loopStart = extraEntry.movieStart;
+ _owner->loopExtraSequence(extraID);
+}
+
+void Caldoria4DSystem::useIdleTime() {
+ if (_whichMenu == k4DShuttingDown) {
+ TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart;
+ ExtraID extraID;
+
+ if (movieTime < kSwitchable1Stop)
+ extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][0];
+ else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop)
+ extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][1];
+ else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop)
+ extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][2];
+ else
+ extraID = 0xffffffff;
+
+ if (extraID != 0xffffffff) {
+ setSpritesMovie();
+ _loopStart = 0;
+ _owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput);
+ }
+ } else if (_clickedHotspotID != kNoHotSpotID) {
+ TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart;
+ ExtraID extraID;
+
+ if (movieTime < kSwitchable1Stop) {
+ extraID = s_transitionExtras0[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID];
+ _clickedHotspotID = kNoHotSpotID;
+ } else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) {
+ extraID = s_transitionExtras1[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID];
+ _clickedHotspotID = kNoHotSpotID;
+ } else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) {
+ extraID = s_transitionExtras2[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID];
+ _clickedHotspotID = kNoHotSpotID;
+ } else
+ extraID = 0xffffffff;
+
+ if (extraID != 0xffffffff) {
+ switch (extraID) {
+ case k4DDesert0ToIsland0:
+ case k4DMountain0ToIsland0:
+ case k4DDesert1ToIsland0:
+ case k4DMountain1ToIsland0:
+ case k4DDesert2ToIsland0:
+ case k4DMountain2ToIsland0:
+ _videoChoice = k4DIslandChoice;
+ break;
+ case k4DIsland0ToDesert0:
+ case k4DMountain0ToDesert0:
+ case k4DIsland1ToDesert0:
+ case k4DMountain1ToDesert0:
+ case k4DIsland2ToDesert0:
+ case k4DMountain2ToDesert0:
+ _videoChoice = k4DDesertChoice;
+ break;
+ case k4DDesert0ToMountain0:
+ case k4DIsland0ToMountain0:
+ case k4DIsland1ToMountain0:
+ case k4DDesert1ToMountain0:
+ case k4DIsland2ToMountain0:
+ case k4DDesert2ToMountain0:
+ _videoChoice = k4DMountainChoice;
+ break;
+ }
+
+ setSpritesMovie();
+ _loopStart = 0;
+ _owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput);
+ }
+ }
+}
+
+void Caldoria4DSystem::initInteraction() {
+ setSpritesMovie();
+
+ _owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff");
+ loopExtra(k4DIslandLoop);
+}
+
+void Caldoria4DSystem::closeInteraction() {
+ stopIdling();
+ _neighborhoodNotification->cancelNotification(this);
+ _4DSpritesMovie.releaseMovie();
+ _owner->loadAmbientLoops();
+}
+
+void Caldoria4DSystem::setSpritesMovie() {
+ if (_whichMenu == k4DShuttingDown)
+ _4DSpritesMovie.setTime(_4DSpritesScale * k4DIslandChoice);
+ else if (_whichMenu == k4DVideoMenu)
+ _4DSpritesMovie.setTime(_4DSpritesScale * _videoChoice);
+ else if (_whichMenu == k4DAudioMenu)
+ _4DSpritesMovie.setTime(_4DSpritesScale * _audioChoice);
+
+ _4DSpritesMovie.redrawMovieWorld();
+}
+
+void Caldoria4DSystem::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (input.downButtonAnyDown())
+ return;
+ if (input.anyDirectionInput())
+ shutDown4DSystem();
+ else
+ GameInteraction::handleInput(input, cursorSpot);
+}
+
+void Caldoria4DSystem::activateHotspots() {
+ GameInteraction::activateHotspots();
+ if (_whichMenu == k4DAudioMenu)
+ g_allHotspots.activateOneHotspot(kCa4DChoice4SpotID);
+}
+
+void Caldoria4DSystem::clickInHotspot(const Input &input, const Hotspot *spot) {
+ switch (spot->getObjectID()) {
+ case kCa4DVisualSpotID:
+ if (_whichMenu == k4DAudioMenu) {
+ _whichMenu = k4DVideoMenu;
+ setSpritesMovie();
+ }
+ break;
+ case kCa4DAudioSpotID:
+ if (_whichMenu == k4DVideoMenu) {
+ _whichMenu = k4DAudioMenu;
+ setSpritesMovie();
+ }
+ break;
+ case kCa4DChoice1SpotID:
+ if (_whichMenu == k4DVideoMenu)
+ makeIslandChoice();
+ else if (_whichMenu == k4DAudioMenu)
+ makeRockChoice();
+ break;
+ case kCa4DChoice2SpotID:
+ if (_whichMenu == k4DVideoMenu)
+ makeDesertChoice();
+ else if (_whichMenu == k4DAudioMenu)
+ makeOrchestralChoice();
+ break;
+ case kCa4DChoice3SpotID:
+ if (_whichMenu == k4DVideoMenu)
+ makeMountainChoice();
+ else if (_whichMenu == k4DAudioMenu)
+ makeRhythmsChoice();
+ break;
+ case kCa4DChoice4SpotID:
+ if (_whichMenu == k4DAudioMenu)
+ makeAcousticChoice();
+ else
+ _owner->playSpotSoundSync(kCaldoria4DBlankChoiceIn, kCaldoria4DBlankChoiceOut);
+ break;
+ default:
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+void Caldoria4DSystem::receiveNotification(Notification *, const NotificationFlags) {
+ if (_whichMenu == k4DShuttingDown) {
+ _owner->requestDeleteCurrentInteraction();
+ } else {
+ uint32 extraID;
+
+ switch (_videoChoice) {
+ case k4DIslandChoice:
+ extraID = k4DIslandLoop;
+ break;
+ case k4DDesertChoice:
+ extraID = k4DDesertLoop;
+ break;
+ case k4DMountainChoice:
+ extraID = k4DMountainLoop;
+ break;
+ default:
+ extraID = 0xffffffff;
+ break;
+ }
+
+ if (extraID != 0xffffffff)
+ loopExtra(extraID);
+ }
+}
+
+void Caldoria4DSystem::makeIslandChoice() {
+ if (_videoChoice != k4DIslandChoice && _clickedHotspotID == kNoHotSpotID)
+ _clickedHotspotID = kCa4DChoice1SpotID;
+}
+
+void Caldoria4DSystem::makeDesertChoice() {
+ if (_videoChoice != k4DDesertChoice && _clickedHotspotID == kNoHotSpotID)
+ _clickedHotspotID = kCa4DChoice2SpotID;
+}
+
+void Caldoria4DSystem::makeMountainChoice() {
+ if (_videoChoice != k4DMountainChoice && _clickedHotspotID == kNoHotSpotID)
+ _clickedHotspotID = kCa4DChoice3SpotID;
+}
+
+void Caldoria4DSystem::makeRockChoice() {
+ if (_audioChoice != k4DRockChoice) {
+ _audioChoice = k4DRockChoice;
+ setSpritesMovie();
+ _owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff");
+ }
+}
+
+void Caldoria4DSystem::makeOrchestralChoice() {
+ if (_audioChoice != k4DOrchestralChoice) {
+ _audioChoice = k4DOrchestralChoice;
+ setSpritesMovie();
+ _owner->loadLoopSound1("Sounds/Caldoria/Orchestral.aiff");
+ }
+}
+
+void Caldoria4DSystem::makeRhythmsChoice() {
+ if (_audioChoice != k4DRhythmsChoice) {
+ _audioChoice = k4DRhythmsChoice;
+ setSpritesMovie();
+ _owner->loadLoopSound1("Sounds/Caldoria/Rhythms.aiff");
+ }
+}
+
+void Caldoria4DSystem::makeAcousticChoice() {
+ if (_audioChoice != k4DAcousticChoice) {
+ _audioChoice = k4DAcousticChoice;
+ setSpritesMovie();
+ _owner->loadLoopSound1("Sounds/Caldoria/Acoustic.aiff");
+ }
+}
+
+void Caldoria4DSystem::shutDown4DSystem() {
+ _whichMenu = k4DShuttingDown;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h
new file mode 100644
index 0000000000..1c5fa44b90
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H
+#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class Neighborhood;
+
+class Caldoria4DSystem : public GameInteraction, private Idler, public NotificationReceiver {
+public:
+ Caldoria4DSystem(Neighborhood *);
+ virtual ~Caldoria4DSystem();
+
+ void shutDown4DSystem();
+
+protected:
+ void openInteraction();
+ void initInteraction();
+ void closeInteraction();
+
+ void handleInput(const Input &, const Hotspot *);
+ void activateHotspots();
+ void clickInHotspot(const Input &, const Hotspot *);
+ void receiveNotification(Notification *, const NotificationFlags);
+ void setSpritesMovie();
+ void makeIslandChoice();
+ void makeRockChoice();
+ void makeMountainChoice();
+ void makeOrchestralChoice();
+ void makeDesertChoice();
+ void makeRhythmsChoice();
+ void makeAcousticChoice();
+
+ void useIdleTime();
+ void loopExtra(const ExtraID);
+
+ Movie _4DSpritesMovie;
+ TimeScale _4DSpritesScale;
+ uint _whichMenu;
+ uint _videoChoice;
+ uint _audioChoice;
+ Notification *_neighborhoodNotification;
+ TimeValue _loopStart;
+ HotSpotID _clickedHotspotID;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp
new file mode 100644
index 0000000000..abf34d3863
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp
@@ -0,0 +1,1442 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/caldoria/caldoria.h"
+#include "pegasus/neighborhood/caldoria/caldoriabomb.h"
+
+namespace Pegasus {
+
+// Bomb game PICTs:
+
+static const uint16 kYellowBombPICTBaseID = 700;
+static const uint16 kRedBombPICTBaseID = 709;
+static const uint16 kTimerLeftPICTID = 718;
+static const uint16 kTimerRightPICTID = 719;
+
+static const uint32 kFlashOnTime = 20;
+static const uint32 kFlashOffTime = 10;
+
+static const uint32 kOnTime1 = kFlashOnTime;
+static const uint32 kOffTime1 = kOnTime1 + kFlashOffTime;
+static const uint32 kOnTime2 = kOffTime1 + kFlashOnTime;
+static const uint32 kOffTime2 = kOnTime2 + kFlashOffTime;
+static const uint32 kOnTime3 = kOffTime2 + kFlashOnTime;
+static const uint32 kOffTime3 = kOnTime3 + kFlashOffTime;
+static const uint32 kOnTime4 = kOffTime3 + kFlashOnTime;
+
+static const HotSpotID kVertextHotSpotBaseID = 10000;
+
+static const CoordType kVertextHotSpotWidth = 24;
+static const CoordType kVertextHotSpotHeight = 24;
+
+static const NotificationFlags kBombTimerExpiredFlag = 1;
+
+static const VertexType kBombLevelOne[] = {
+ 0, 1, 0, 1, 0, // hot vertices first.
+ 1, 1, 0, 1, 1,
+ 1, 1, 0, 1, 0,
+ 1, 1, 0, 1, 1,
+ 0, 1, 0, 1, 0,
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+
+ 9, // 9 edges in this level
+
+ kEdgeOneFourth,
+ 3,
+ 1, 2, 3,
+ 0, 0,
+
+ kEdgeOneFourth,
+ 5,
+ 5, 6, 7, 8, 9,
+ 0, 0, 0, 0,
+
+ kEdgeOneFourth,
+ 4,
+ 10, 11, 12, 13,
+ 0, 0, 0,
+
+ kEdgeOneFourth,
+ 5,
+ 15, 16, 17, 18, 19,
+ 0, 0, 0, 0,
+
+ kEdgeOneFourth,
+ 3,
+ 21, 22, 23,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 5, 10, 15,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 5,
+ 1, 6, 11, 16, 21,
+ 0, 0, 0, 0,
+
+ kEdgeOneHalf,
+ 5,
+ 3, 8, 13, 18, 23,
+ 0, 0, 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 9, 14, 19,
+ 0, 0
+};
+
+static const VertexType kBombLevelTwo[] = {
+ 0, 1, 0, 1, 0,
+ 1, 1, 1, 0, 1,
+ 0, 0, 0, 1, 0,
+ 1, 1, 1, 0, 1,
+ 0, 1, 0, 1, 0,
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+
+ 15,
+
+ kEdgeOneEighth,
+ 2,
+ 5, 1,
+ 0,
+
+ kEdgeOneEighth,
+ 3,
+ 17, 13, 9,
+ 0, 0,
+
+ kEdgeOneEighth,
+ 2,
+ 23, 19,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 3, 9,
+ 0,
+
+ kEdgeThreeEighths,
+ 3,
+ 7, 13, 19,
+ 0, 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 15, 21,
+ 0,
+
+ kEdgeOneFourth,
+ 3,
+ 1, 2, 3,
+ 0, 0,
+
+ kEdgeOneFourth,
+ 4,
+ 6, 7, 8, 9,
+ 0, 0, 0,
+
+ kEdgeOneFourth,
+ 4,
+ 16, 17, 18, 19,
+ 0, 0, 0,
+
+ kEdgeOneFourth,
+ 3,
+ 21, 22, 23,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 5, 10, 15,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 2,
+ 1, 6,
+ 0,
+
+ kEdgeOneHalf,
+ 3,
+ 7, 12, 17,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 9, 14, 19,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 2,
+ 16, 21,
+ 0
+};
+
+static const VertexType kBombLevelThree[] = {
+ 0, 1, 0, 1, 0,
+ 1, 1, 1, 1, 1,
+ 0, 1, 1, 0, 0,
+ 1, 1, 1, 1, 1,
+ 0, 1, 0, 1, 0,
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+
+ 22,
+
+ kEdgeThreeSixteenths,
+ 3,
+ 15, 12, 9,
+ 0, 0,
+
+ kEdgeFiveSixteenths,
+ 3,
+ 5, 12, 19,
+ 0, 0,
+
+ kEdgeOneEighth,
+ 2,
+ 5, 1,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 7, 3,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 15, 11,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 21, 17,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 23, 19,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 1, 7,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 3, 9,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 5, 11,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 15, 21,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 17, 23,
+ 0,
+
+ kEdgeOneFourth,
+ 3,
+ 1, 2, 3,
+ 0, 0,
+
+ kEdgeOneFourth,
+ 2,
+ 5, 6,
+ 0,
+
+ kEdgeOneFourth,
+ 2,
+ 8, 9,
+ 0,
+
+ kEdgeOneFourth,
+ 2,
+ 15, 16,
+ 0,
+
+ kEdgeOneFourth,
+ 2,
+ 18, 19,
+ 0,
+
+ kEdgeOneFourth,
+ 3,
+ 21, 22, 23,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 2,
+ 1, 6,
+ 0,
+
+ kEdgeOneHalf,
+ 2,
+ 3, 8,
+ 0,
+
+ kEdgeOneHalf,
+ 2,
+ 16, 21,
+ 0,
+
+ kEdgeOneHalf,
+ 2,
+ 18, 23,
+ 0
+};
+
+static const VertexType kBombLevelFour[] = {
+ 1, 1, 1, 1, 0,
+ 1, 1, 0, 1, 1,
+ 1, 0, 1, 0, 1,
+ 1, 1, 0, 1, 1,
+ 0, 1, 1, 1, 1,
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+
+ 19,
+
+ kEdgeOneEighth,
+ 2,
+ 5, 1,
+ 0,
+
+ kEdgeOneEighth,
+ 3,
+ 10, 6, 2,
+ 0, 0,
+
+ kEdgeOneEighth,
+ 3,
+ 16, 12, 8,
+ 0, 0,
+
+ kEdgeOneEighth,
+ 3,
+ 22, 18, 14,
+ 0, 0,
+
+ kEdgeOneEighth,
+ 2,
+ 23, 19,
+ 0,
+
+ kEdgeThreeEighths,
+ 3,
+ 2, 8, 14,
+ 0, 0,
+
+ kEdgeThreeEighths,
+ 3,
+ 10, 16, 22,
+ 0, 0,
+
+ kEdgeOneFourth,
+ 4,
+ 0, 1, 2, 3,
+ 0, 0, 0,
+
+ kEdgeOneFourth,
+ 2,
+ 5, 6,
+ 0,
+
+ kEdgeOneFourth,
+ 2,
+ 8, 9,
+ 0,
+
+ kEdgeOneFourth,
+ 2,
+ 15, 16,
+ 0,
+
+ kEdgeOneFourth,
+ 2,
+ 18, 19,
+ 0,
+
+ kEdgeOneFourth,
+ 4,
+ 21, 22, 23, 24,
+ 0, 0, 0,
+
+ kEdgeOneHalf,
+ 4,
+ 0, 5, 10, 15,
+ 0, 0, 0,
+
+ kEdgeOneHalf,
+ 2,
+ 1, 6,
+ 0,
+
+ kEdgeOneHalf,
+ 2,
+ 3, 8,
+ 0,
+
+ kEdgeOneHalf,
+ 4,
+ 9, 14, 19, 24,
+ 0, 0, 0,
+
+ kEdgeOneHalf,
+ 2,
+ 16, 21,
+ 0,
+
+ kEdgeOneHalf,
+ 2,
+ 18, 23,
+ 0
+};
+
+static const VertexType kBombLevelFive[] = {
+ 0, 1, 0, 1, 0,
+ 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1,
+ 0, 1, 0, 1, 0,
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+
+ 19,
+
+ kEdgeOneEighth,
+ 2,
+ 5, 1,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 7, 3,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 13, 9,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 15, 11,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 21, 17,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 23, 19,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 1, 7,
+ 0,
+
+ kEdgeThreeEighths,
+ 4,
+ 5, 11, 17, 23,
+ 0, 0, 0,
+
+ kEdgeThreeEighths,
+ 3,
+ 6, 12, 18,
+ 0, 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 13, 19,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 15, 21,
+ 0,
+
+ kEdgeOneFourth,
+ 5,
+ 5, 6, 7, 8, 9,
+ 0, 0, 0, 0,
+
+ kEdgeOneFourth,
+ 3,
+ 15, 16, 17,
+ 0, 0,
+
+ kEdgeOneFourth,
+ 2,
+ 18, 19,
+ 0,
+
+ kEdgeOneFourth,
+ 3,
+ 21, 22, 23,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 5, 10, 15,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 2,
+ 1, 6,
+ 0,
+
+ kEdgeOneHalf,
+ 3,
+ 11, 16, 21,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 5,
+ 3, 8, 13, 18, 23,
+ 0, 0, 0, 0
+};
+
+static const VertexType kBombLevelSix[] = {
+ 0, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1,
+ 1, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 0,
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+
+ 25,
+
+ kEdgeOneSixteenth,
+ 2,
+ 10, 1,
+ 0,
+
+ kEdgeOneSixteenth,
+ 2,
+ 23, 14,
+ 0,
+
+ kEdgeSevenSixteenths,
+ 2,
+ 3, 14,
+ 0,
+
+ kEdgeSevenSixteenths,
+ 2,
+ 10, 21,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 5, 1,
+ 0,
+
+ kEdgeOneEighth,
+ 3,
+ 10, 6, 2,
+ 0, 0,
+
+ kEdgeOneEighth,
+ 2,
+ 7, 3,
+ 0,
+
+ kEdgeOneEighth,
+ 2,
+ 21, 17,
+ 0,
+
+ kEdgeOneEighth,
+ 3,
+ 22, 18, 14,
+ 0, 0,
+
+ kEdgeOneEighth,
+ 2,
+ 23, 19,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 1, 7,
+ 0,
+
+ kEdgeThreeEighths,
+ 3,
+ 2, 8, 14,
+ 0, 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 3, 9,
+ 0,
+
+ kEdgeThreeEighths,
+ 3,
+ 10, 16, 22,
+ 0, 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 15, 21,
+ 0,
+
+ kEdgeThreeEighths,
+ 2,
+ 17, 23,
+ 0,
+
+ kEdgeOneFourth,
+ 3,
+ 1, 2, 3,
+ 0, 0,
+
+ kEdgeOneFourth,
+ 3,
+ 6, 7, 8,
+ 0, 0,
+
+ kEdgeOneFourth,
+ 3,
+ 16, 17, 18,
+ 0, 0,
+
+ kEdgeOneFourth,
+ 3,
+ 21, 22, 23,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 5, 10, 15,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 6, 11, 16,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 5,
+ 2, 7, 12, 17, 22,
+ 0, 0, 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 8, 13, 18,
+ 0, 0,
+
+ kEdgeOneHalf,
+ 3,
+ 9, 14, 19,
+ 0, 0
+};
+
+static const CoordType kBombGridWidth = 140;
+static const CoordType kBombGridHeight = 140;
+
+static const CoordType kDotOriginX = 0;
+static const CoordType kDotOriginY = 0;
+
+static const CoordType kVertOriginX = 2;
+static const CoordType kVertOriginY = 6;
+
+static const CoordType kHorizOriginX = 6;
+static const CoordType kHorizOriginY = 2;
+
+static const CoordType kDiagOriginX = 6;
+static const CoordType kDiagOriginY = 6;
+
+static const int g_originsX[] = {
+ kDiagOriginX,
+ kDiagOriginX,
+ kDiagOriginX,
+ kHorizOriginX,
+ kDiagOriginX,
+ kDiagOriginX,
+ kDiagOriginX,
+ kVertOriginX
+};
+
+static const int g_originsY[] = {
+ kDiagOriginY - 64,
+ kDiagOriginY - 32,
+ kDiagOriginY - 32,
+ kHorizOriginY,
+ kDiagOriginY,
+ kDiagOriginY,
+ kDiagOriginY,
+ kVertOriginY
+};
+
+struct HotVerticesList {
+ int numHotVerts;
+ VertexType hotVerts[25];
+};
+
+CoordType vertToX(VertexType vertex) {
+ return (vertex % 5) * 32;
+}
+
+CoordType vertToY(VertexType vertex) {
+ return (vertex / 5) * 32;
+}
+
+// This function returns the number of edges in the bomb edge list.
+VertexType getNumEdges(BombEdgeList edges) {
+ return edges[50];
+}
+
+// These four functions return pointers into the given edge list.
+
+// getFirstEdge and getNextEdge can be used to iterate across all edges
+// in an edge list. These functions can be used to walk all the edges
+// in a bomb edge list for drawing.
+VertexType *getFirstEdge(BombEdgeList edges) {
+ return &edges[51];
+}
+
+VertexType *getNextEdge(VertexType *anEdge) {
+ return anEdge + *(anEdge + 1) * 2 + 1;
+}
+
+// getVertices returns a pointer to all of the vertices that should are
+// hot. These vertices indicate all the vertices that should be drawn in
+// the game.
+VertexType *getVertices(BombEdgeList edges) {
+ return &edges[0];
+}
+
+// getUsedVertices returns a pointer to the "used" vertices area: the
+// area that keeps track of which vertices have been set by the
+// setVertexUsed used function.
+VertexType *getUsedVertices(BombEdgeList edges) {
+ return &edges[25];
+}
+
+// Useful for saving. Saving the state of the bomb game is as simple as writing
+// out the edge list.
+int getEdgeListSize(BombEdgeList edges) {
+ VertexType numEdges = getNumEdges(edges);
+ VertexType *anEdge = getFirstEdge(edges);
+
+ while (numEdges--)
+ anEdge = getNextEdge(anEdge);
+
+ return anEdge - edges + 4;
+}
+
+// Returns true if the given vertex lies on the given edge.
+bool vertexOnEdge(VertexType *anEdge, VertexType whichVertex) {
+ VertexType numVerts = *++anEdge;
+
+ while (numVerts--)
+ if (*++anEdge == whichVertex)
+ return true;
+
+ return false;
+}
+
+// Given an edge list and a from vertex, this function constructs a list
+// of all vertices that may be clicked on.
+// if fromVertex == -1, all vertices are eligible.
+// otherwise, only vertices on a line from fromVertex are eligible.
+void makeHotVertexList(BombEdgeList edges, VertexType fromVertex, HotVerticesList &hotVertices) {
+ hotVertices.numHotVerts = 0;
+
+ if (fromVertex == -1) {
+ for (VertexType i = 0; i < 25; i++)
+ if (edges[i])
+ hotVertices.hotVerts[hotVertices.numHotVerts++] = i;
+ } else {
+ VertexType numEdges = getNumEdges(edges);
+ VertexType *anEdge = getFirstEdge(edges);
+ hotVertices.hotVerts[hotVertices.numHotVerts++] = fromVertex;
+
+ while (numEdges--) {
+ if (vertexOnEdge(anEdge, fromVertex)) {
+ VertexType *p = anEdge + 1;
+ VertexType numVerts = *p;
+
+ while (numVerts--)
+ if (*++p != fromVertex)
+ hotVertices.hotVerts[hotVertices.numHotVerts++] = *p;
+ }
+
+ anEdge = getNextEdge(anEdge);
+ }
+ }
+}
+
+// Set all edges in the edge list to the value passed in "edgeVal".
+// For drawing purposes, 0 can mean don't draw, and 1 and higher can
+// represent different colors.
+void setAllEdgesUsed(BombEdgeList edges, VertexType used) {
+ VertexType numEdges = getNumEdges(edges);
+ VertexType *anEdge = getFirstEdge(edges);
+
+ while (numEdges--) {
+ VertexType *p1 = anEdge + 1;
+ VertexType numVerts = *p1;
+ p1 += numVerts + 1;
+
+ while (--numVerts)
+ *p1++ = used;
+
+ anEdge = getNextEdge(anEdge);
+ }
+
+ VertexType *p1 = edges;
+ VertexType *p2 = getUsedVertices(edges);
+
+ for (VertexType i = 0; i < 25; i++, p1++, p2++)
+ if (*p1)
+ *p2 = used;
+}
+
+// Same as setAllEdgesUsed, but only affects edges that are already set
+// to a non-zero value.
+void setAllUsedEdgesUsed(BombEdgeList edges, VertexType used) {
+ VertexType numEdges = getNumEdges(edges);
+ VertexType *anEdge = getFirstEdge(edges);
+
+ while (numEdges--) {
+ VertexType *p = anEdge + 1;
+ VertexType numVerts = *p;
+ p += numVerts + 1;
+
+ while (--numVerts) {
+ if (*p)
+ *p = used;
+ ++p;
+ }
+
+ anEdge = getNextEdge(anEdge);
+ }
+
+ VertexType *p = getUsedVertices(edges);
+ for (VertexType i = 0; i < 25; i++, p++)
+ if (*p)
+ *p = used;
+}
+
+// Replace all edges with value "value" with the new value "used".
+void replaceUsedEdges(BombEdgeList edges, VertexType value, VertexType used) {
+ VertexType numEdges = getNumEdges(edges);
+ VertexType *anEdge = getFirstEdge(edges);
+
+ while (numEdges--) {
+ VertexType *p = anEdge + 1;
+ VertexType numVerts = *p;
+ p += numVerts + 1;
+
+ while (--numVerts) {
+ if (*p == value)
+ *p = used;
+
+ p++;
+ }
+
+ anEdge = getNextEdge(anEdge);
+ }
+
+ VertexType *p = getUsedVertices(edges);
+ for (VertexType i = 0; i < 25; i++, p++)
+ if (*p == value)
+ *p = used;
+}
+
+// Set a vertex's value to "used".
+void setVertexUsed(BombEdgeList edges, VertexType whichVertex, VertexType value) {
+ *(getUsedVertices(edges) + whichVertex) = value;
+}
+
+// Mark an edge in the given list between the two vertices as "used". This marks
+// all inbetween vertices as well, even if the vertex is not marked as a "hot"
+// vertex in the hot vertex section. Returns true if doing this operation
+// crosses an already marked edge.
+bool setEdgeUsed(BombEdgeList edges, VertexType fromVertex, VertexType toVertex) {
+ VertexType numEdges = getNumEdges(edges);
+ VertexType *anEdge = getFirstEdge(edges);
+ bool crossed = false;
+
+ while (numEdges--) {
+ VertexType *p = anEdge;
+ VertexType numVerts = *++p;
+ VertexType *fromPtr = 0;
+ VertexType *toPtr = 0;
+ VertexType i = numVerts;
+ p++;
+
+ while (i--) {
+ if (*p == fromVertex)
+ fromPtr = p;
+ else if (*p == toVertex)
+ toPtr = p;
+
+ if (fromPtr && toPtr) {
+ // Found the edge...
+ if (fromPtr > toPtr) {
+ p = fromPtr;
+ fromPtr = toPtr;
+ toPtr = p;
+ }
+
+ p = fromPtr + numVerts;
+
+ for (i = toPtr - fromPtr; i > 0; i--, p++) {
+ ++(*p);
+
+ if (*p == 2)
+ crossed = true;
+ }
+
+ VertexType *verts = getVertices(edges);
+ VertexType *usedVerts = getUsedVertices(edges);
+ *(usedVerts + *fromPtr) = 1;
+
+ for (p = fromPtr + 1; p != toPtr; p++)
+ if (*(verts + *p))
+ *(usedVerts + *p) = 1;
+
+ *(usedVerts + *toPtr) = 1;
+ return crossed;
+ }
+
+ p++;
+ }
+
+ anEdge = getNextEdge(anEdge);
+ }
+
+ return false;
+}
+
+// Return true if all edges are used. Can be used to determine when the bomb
+// game is over.
+bool allEdgesUsed(BombEdgeList edges) {
+ VertexType numEdges = getNumEdges(edges);
+ VertexType *anEdge = getFirstEdge(edges);
+
+ while (numEdges--) {
+ VertexType *p = anEdge + 1;
+ VertexType numVerts = *p;
+ p += numVerts + 1;
+
+ while (--numVerts) {
+ if (!*p)
+ return false;
+
+ ++p;
+ }
+
+ anEdge = getNextEdge(anEdge);
+ }
+
+ return true;
+}
+
+BombGrid::BombGrid(const DisplayElementID id) : Picture(id) {
+ Common::Rect bounds(0, 0, kBombGridWidth, kBombGridHeight);
+
+ allocateSurface(bounds);
+ setBounds(bounds);
+ _surface->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff));
+
+ _transparent = true;
+
+ _yellowDot.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID, true);
+ _yellowOneSixteenth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 1, true);
+ _yellowOneEighth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 2, true);
+ _yellowThreeSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 3, true);
+ _yellowOneFourth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 4, true);
+ _yellowFiveSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 5, true);
+ _yellowThreeEighths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 6, true);
+ _yellowSevenSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 7, true);
+ _yellowOneHalf.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 8, true);
+
+ _redDot.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID, true);
+ _redOneSixteenth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 1, true);
+ _redOneEighth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 2, true);
+ _redThreeSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 3, true);
+ _redOneFourth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 4, true);
+ _redFiveSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 5, true);
+ _redThreeEighths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 6, true);
+ _redSevenSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 7, true);
+ _redOneHalf.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 8, true);
+}
+
+void BombGrid::drawEdges(BombEdgeList edges) {
+ GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx;
+ gfx->setCurSurface(_surface);
+
+ _surface->fillRect(Common::Rect(0, 0, kBombGridWidth, kBombGridHeight), g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff));
+
+ Frame *yellowStuff = &_yellowDot;
+ Frame *redStuff = &_redDot;
+ VertexType numEdges = getNumEdges(edges);
+ VertexType *anEdge = getFirstEdge(edges);
+ VertexType i, *p;
+
+ Common::Rect bounds;
+ getSurfaceBounds(bounds);
+
+ while (numEdges--) {
+ p = anEdge;
+ VertexType edgeDirection = *p++;
+ VertexType numVerts = *p++;
+ VertexType numSegs = numVerts - 1;
+
+ for (i = 0; i < numSegs; i++, p++) {
+ if (*(p + numVerts) > 0 && *(p + numVerts) < 4) {
+ Frame *drawStuff;
+
+ if (*(p + numVerts) == 2)
+ drawStuff = redStuff;
+ else
+ drawStuff = yellowStuff;
+
+ int x = vertToX(*p) + g_originsX[edgeDirection];
+ int y = vertToY(*p) + g_originsY[edgeDirection];
+
+ Common::Rect r1;
+ drawStuff[edgeDirection + 1].getSurfaceBounds(r1);
+ Common::Rect r2 = r1;
+ r2.moveTo(x, y);
+ drawStuff[edgeDirection + 1].drawImage(r1, r2);
+ }
+ }
+
+ anEdge = getNextEdge(anEdge);
+ }
+
+ for (i = 0, p = getUsedVertices(edges); i < 25; i++, p++) {
+ if (*p > 0 && *p < 4) {
+ Frame *drawStuff;
+
+ if (*p == 2)
+ drawStuff = redStuff;
+ else
+ drawStuff = yellowStuff;
+
+ int x = vertToX(i) + kDotOriginX;
+ int y = vertToY(i) + kDotOriginY;
+
+ Common::Rect r1;
+ drawStuff->getSurfaceBounds(r1);
+ Common::Rect r2 = r1;
+ r2.moveTo(x, y);
+ drawStuff->drawImage(r1, r2);
+ }
+ }
+
+ triggerRedraw();
+ gfx->setCurSurface(gfx->getWorkArea());
+}
+
+BombTimer::BombTimer(const DisplayElementID id) : IdlerAnimation(id) {
+ _middle = -1;
+ _leftImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTimerLeftPICTID);
+ _rightImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTimerRightPICTID);
+
+ Common::Rect r;
+ _leftImage.getSurfaceBounds(r);
+ setBounds(r);
+}
+
+void BombTimer::draw(const Common::Rect &updateRect) {
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ Common::Rect r1 = bounds;
+ r1.right = _middle;
+ r1 = r1.findIntersectingRect(updateRect);
+
+ if (!r1.isEmpty()) {
+ Common::Rect r2 = r1;
+ r2.moveTo(r1.left - bounds.left, r1.top - bounds.top);
+ _leftImage.copyToCurrentPort(r2, r1);
+ }
+
+ r1 = bounds;
+ r1.left = _middle;
+ r1 = r1.findIntersectingRect(updateRect);
+
+ if (!r1.isEmpty()) {
+ Common::Rect r2 = r1;
+ r2.moveTo(r1.left - bounds.left, r1.top - bounds.top);
+ _rightImage.copyToCurrentPort(r2, r1);
+ }
+}
+
+void BombTimer::timeChanged(const TimeValue newTime) {
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ int newMiddle = bounds.right - bounds.width() * newTime / getDuration();
+ if (newMiddle != _middle) {
+ _middle = newMiddle;
+ triggerRedraw();
+ }
+}
+
+#define CREATE_BOMB_LEVEL(num, data) \
+ _bombLevel[num] = new VertexType[sizeof(data)]; \
+ memcpy(_bombLevel[num], data, sizeof(data))
+
+CaldoriaBomb::CaldoriaBomb(Neighborhood *owner, NotificationManager *manager) :
+ GameInteraction(kCaldoriaBombInteractionID, owner), _grid(kCaldoriaBombGridID),
+ _timer(kCaldoriaBombTimerID), _timerNotification(kCaldoriaBombTimerNotificationID, manager) {
+ CREATE_BOMB_LEVEL(0, kBombLevelOne);
+ CREATE_BOMB_LEVEL(1, kBombLevelTwo);
+ CREATE_BOMB_LEVEL(2, kBombLevelThree);
+ CREATE_BOMB_LEVEL(3, kBombLevelFour);
+ CREATE_BOMB_LEVEL(4, kBombLevelFive);
+ CREATE_BOMB_LEVEL(5, kBombLevelSix);
+ _currentLevel = 0;
+}
+
+#undef CREATE_BOMB_LEVEL
+
+CaldoriaBomb::~CaldoriaBomb() {
+ for (int i = 0; i < 6; i++)
+ delete[] _bombLevel[i];
+}
+
+void CaldoriaBomb::openInteraction() {
+ _grid.moveElementTo(kCaldoriaBombGridLeft, kCaldoriaBombGridTop);
+ _grid.setDisplayOrder(kCaldoriaBombGridOrder);
+ _grid.startDisplaying();
+
+ _timer.moveElementTo(kCaldoriaBombTimerLeft, kCaldoriaBombTimerTop);
+ _timer.setDisplayOrder(kCaldoriaBombTimerOrder);
+ _timer.startDisplaying();
+ _timer.setSegment(0, kTenMinutesPerFifteenTicks, kFifteenTicksPerSecond);
+ _timer.setTime(0);
+
+ _timerNotification.notifyMe(this, kBombTimerExpiredFlag, kBombTimerExpiredFlag);
+ _timerCallBack.setNotification(&_timerNotification);
+ _timerCallBack.initCallBack(&_timer, kCallBackAtExtremes);
+ _timerCallBack.setCallBackFlag(kBombTimerExpiredFlag);
+
+ Common::Rect r(0, 0, kVertextHotSpotWidth, kVertextHotSpotHeight);
+
+ for (VertexType i = 0; i < 25; i++) {
+ _vertexHotspot[i] = new Hotspot(i + kVertextHotSpotBaseID);
+ r.moveTo(vertToX(i) + kCaldoriaBombGridLeft - kVertextHotSpotWidth / 2 + 6,
+ vertToY(i) + kCaldoriaBombGridTop - kVertextHotSpotHeight / 2 + 6);
+ _vertexHotspot[i]->setArea(r);
+ _vertexHotspot[i]->setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ g_allHotspots.push_back(_vertexHotspot[i]);
+ }
+
+ _neighborhoodNotification = _owner->getNeighborhoodNotification();
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
+}
+
+void CaldoriaBomb::initInteraction() {
+ _owner->loadLoopSound1("");
+ _owner->startExtraSequence(kCaldoria56BombStage1, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void CaldoriaBomb::closeInteraction() {
+ _timer.stop();
+ _timer.hide();
+ _timer.stopDisplaying();
+ _grid.hide();
+ _grid.stopDisplaying();
+
+ // The original did not do this, but we need it here
+ // Not sure why the original worked without this; probably
+ // related to the way the List code worked in CodeWarrior.
+ // If this is not here, the notifications will later attempt
+ // to remove itself from this receiver causing a very nasty
+ // crash.
+ _timerNotification.cancelNotification(this);
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void CaldoriaBomb::startBombAmbient(Common::String ambient) {
+ _owner->loadLoopSound1(ambient);
+}
+
+void CaldoriaBomb::receiveNotification(Notification *notification, const NotificationFlags) {
+ if (notification == _neighborhoodNotification) {
+ switch (_owner->getLastExtra()) {
+ case kCaldoria56BombStage1:
+ _grid.show();
+ _timer.show();
+ _timerCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _timer.start();
+ _currentLevel = 0;
+ _lastVertex = -1;
+ startBombAmbient("Sounds/Caldoria/BmbLoop1.22K.AIFF");
+ break;
+ case kCaldoria56BombStage2:
+ case kCaldoria56BombStage3:
+ case kCaldoria56BombStage4:
+ case kCaldoria56BombStage5:
+ case kCaldoria56BombStage6:
+ _grid.show();
+ _currentLevel++;
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -1;
+ startBombAmbient(Common::String::format("Sounds/Caldoria/BmbLoop%d.22K.AIFF", _owner->getLastExtra() - kCaldoria56BombStage1 + 1));
+ break;
+ case kCaldoria56BombStage7:
+ _owner->requestDeleteCurrentInteraction();
+ GameState.setCaldoriaBombDisarmed(true);
+ GameState.setScoringDisarmedNuke(true);
+ _owner->loadAmbientLoops();
+ break;
+ }
+ } else if (notification == &_timerNotification) {
+ _grid.hide();
+ _timer.stop();
+ _timer.hide();
+ _owner->loadLoopSound1("");
+ _owner->playDeathExtra(kCaldoria56BombExplodes, kDeathNuclearExplosion);
+ }
+}
+
+void CaldoriaBomb::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ if (_currentLevel != -1 && _lastVertex >= -1) {
+ HotVerticesList hotVertices;
+ makeHotVertexList(_bombLevel[_currentLevel], _lastVertex, hotVertices);
+
+ for (VertexType i = 0; i < hotVertices.numHotVerts; i++)
+ g_allHotspots.activateOneHotspot(hotVertices.hotVerts[i] + kVertextHotSpotBaseID);
+ }
+}
+
+void CaldoriaBomb::clickInHotspot(const Input &input, const Hotspot *hotspot) {
+ int clickedVertex = (int)hotspot->getObjectID() - (int)kVertextHotSpotBaseID;
+
+ if (clickedVertex >= 0 && clickedVertex < 25) {
+ if (_lastVertex != -1 && setEdgeUsed(_bombLevel[_currentLevel], _lastVertex, clickedVertex)) {
+ clickedVertex = -2;
+ _flashTime = tickCount();
+ } else if (allEdgesUsed(_bombLevel[_currentLevel])) {
+ setVertexUsed(_bombLevel[_currentLevel], clickedVertex, 1);
+ clickedVertex = -20;
+ _flashTime = tickCount();
+ } else {
+ setVertexUsed(_bombLevel[_currentLevel], clickedVertex, 2);
+ }
+
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = clickedVertex;
+ } else {
+ GameInteraction::clickInHotspot(input, hotspot);
+ }
+}
+
+InputBits CaldoriaBomb::getInputFilter() {
+ // Disallow arrow buttons.
+ return GameInteraction::getInputFilter() & kFilterAllButtons;
+}
+
+void CaldoriaBomb::handleInput(const Input &input, const Hotspot *hotspot) {
+ GameInteraction::handleInput(input, hotspot);
+
+ switch (_lastVertex) {
+ case -2: // Flash back to yellow.
+ if (tickCount() > _flashTime + kOnTime1) {
+ replaceUsedEdges(_bombLevel[_currentLevel], 2, 3);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -3;
+ }
+ break;
+ case -3: // Flash back to red.
+ if (tickCount() > _flashTime + kOffTime1) {
+ replaceUsedEdges(_bombLevel[_currentLevel], 3, 2);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -4;
+ }
+ break;
+ case -4: // Flash all to yellow.
+ if (tickCount() > _flashTime + kOnTime2) {
+ setAllUsedEdgesUsed(_bombLevel[_currentLevel], 1);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -5;
+ }
+ break;
+ case -5: // Flash all to red.
+ if (tickCount() > _flashTime + kOffTime2) {
+ setAllUsedEdgesUsed(_bombLevel[_currentLevel], 2);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -6;
+ }
+ break;
+ case -6: // Flash all to yellow.
+ if (tickCount() > _flashTime + kOnTime3) {
+ setAllUsedEdgesUsed(_bombLevel[_currentLevel], 1);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -7;
+ }
+ break;
+ case -7: // Flash all to red.
+ if (tickCount() > _flashTime + kOffTime3) {
+ setAllUsedEdgesUsed(_bombLevel[_currentLevel], 2);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -8;
+ }
+ break;
+ case -8: // Restore to normal.
+ if (tickCount() > _flashTime + kOnTime4) {
+ setAllEdgesUsed(_bombLevel[_currentLevel], 0);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -1;
+ }
+ break;
+
+ // Flash grid after success.
+ case -20: // Flash off.
+ if (tickCount() > _flashTime + kOnTime1) {
+ setAllEdgesUsed(_bombLevel[_currentLevel], 4);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -21;
+ }
+ break;
+ case -21: // Flash on.
+ if (tickCount() > _flashTime + kOffTime1) {
+ setAllEdgesUsed(_bombLevel[_currentLevel], 1);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -22;
+ }
+ break;
+ case -22: // Flash off.
+ if (tickCount() > _flashTime + kOnTime2) {
+ setAllEdgesUsed(_bombLevel[_currentLevel], 4);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -23;
+ }
+ break;
+ case -23: // Flash on.
+ if (tickCount() > _flashTime + kOffTime2) {
+ setAllEdgesUsed(_bombLevel[_currentLevel], 1);
+ _grid.drawEdges(_bombLevel[_currentLevel]);
+ _lastVertex = -24;
+ }
+ break;
+ case -24:
+ if (tickCount() > _flashTime + kOnTime3) {
+ _grid.hide();
+ _lastVertex = -1;
+ _owner->loadLoopSound1("");
+
+ switch (_currentLevel) {
+ case 0:
+ _owner->startExtraSequence(kCaldoria56BombStage2, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 1:
+ _owner->startExtraSequence(kCaldoria56BombStage3, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 2:
+ _owner->startExtraSequence(kCaldoria56BombStage4, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 3:
+ _owner->startExtraSequence(kCaldoria56BombStage5, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 4:
+ _owner->startExtraSequence(kCaldoria56BombStage6, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 5:
+ _timer.stop();
+ _grid.hide();
+ _timer.hide();
+ _owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+long CaldoriaBomb::getNumHints() {
+ return 2;
+}
+
+Common::String CaldoriaBomb::getHintMovie(uint hintNum) {
+ return (hintNum == 1) ? "Images/AI/Caldoria/X56EH2" : "Images/AI/Caldoria/X56EH3";
+}
+
+bool CaldoriaBomb::canSolve() {
+ return true;
+}
+
+void CaldoriaBomb::doSolve() {
+ _timer.stop();
+ _grid.hide();
+ _timer.hide();
+ _owner->loadLoopSound1("");
+ _owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.h b/engines/pegasus/neighborhood/caldoria/caldoriabomb.h
new file mode 100644
index 0000000000..5bb39b4122
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.h
@@ -0,0 +1,156 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIABOMB_H
+#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIABOMB_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/notification.h"
+#include "pegasus/surface.h"
+
+namespace Pegasus {
+
+/*
+ Edge list is arranged as follows:
+
+ all values in the edge list are bytes.
+
+ all vertices are numbers between 0 and 24. x coordinate of vertex is vertex % 5,
+ and y coordinate is vertex / 5.
+
+ an edge is
+ a direction code
+ a number of vertices in the edge
+ an array of vertices -- all vertices along the edge, whether or not they're
+ clickable.
+ an array of bools (bytes) indicating that a portion of the edge is
+ traversed (and should be drawn). the number of bools is one less than
+ the number of vertices.
+
+ an edge list is
+ an array of 25 bools indicating which vertex is clickable.
+ an array of 25 bools indicating which vertex is used (drawn).
+ a number of edges
+ an array of edges.
+
+ a hot vertex list is
+ a number of vertices
+ an array of 25 vertices
+
+*/
+
+typedef int8 VertexType;
+typedef VertexType *BombEdgeList;
+
+static const VertexType kEdgeOneSixteenth = 0;
+static const VertexType kEdgeOneEighth = 1;
+static const VertexType kEdgeThreeSixteenths = 2;
+static const VertexType kEdgeOneFourth = 3;
+static const VertexType kEdgeFiveSixteenths = 4;
+static const VertexType kEdgeThreeEighths = 5;
+static const VertexType kEdgeSevenSixteenths = 6;
+static const VertexType kEdgeOneHalf = 7;
+
+class BombTimer : public IdlerAnimation {
+public:
+ BombTimer(const DisplayElementID);
+ virtual ~BombTimer() {}
+
+ void draw(const Common::Rect &);
+
+protected:
+ void timeChanged(const TimeValue);
+
+ int _middle;
+ Surface _leftImage, _rightImage;
+};
+
+class BombGrid : public Picture {
+public:
+ BombGrid(const DisplayElementID);
+ virtual ~BombGrid() {}
+
+ void drawEdges(BombEdgeList);
+
+protected:
+ Frame _yellowDot;
+ Frame _yellowOneSixteenth;
+ Frame _yellowOneEighth;
+ Frame _yellowThreeSixteenths;
+ Frame _yellowOneFourth;
+ Frame _yellowFiveSixteenths;
+ Frame _yellowThreeEighths;
+ Frame _yellowSevenSixteenths;
+ Frame _yellowOneHalf;
+ Frame _redDot;
+ Frame _redOneSixteenth;
+ Frame _redOneEighth;
+ Frame _redThreeSixteenths;
+ Frame _redOneFourth;
+ Frame _redFiveSixteenths;
+ Frame _redThreeEighths;
+ Frame _redSevenSixteenths;
+ Frame _redOneHalf;
+};
+
+class Hotspot;
+
+class CaldoriaBomb : public GameInteraction, public NotificationReceiver {
+public:
+ CaldoriaBomb(Neighborhood *, NotificationManager *);
+ virtual ~CaldoriaBomb();
+
+ long getNumHints();
+ Common::String getHintMovie(uint);
+ void doSolve();
+ bool canSolve();
+
+protected:
+ void openInteraction();
+ void initInteraction();
+ void closeInteraction();
+ void receiveNotification(Notification *, const NotificationFlags);
+ void activateHotspots();
+ void clickInHotspot(const Input &, const Hotspot *);
+ void handleInput(const Input &, const Hotspot *);
+ InputBits getInputFilter();
+ void startBombAmbient(Common::String);
+
+ Notification *_neighborhoodNotification;
+ BombGrid _grid;
+ BombTimer _timer;
+ BombEdgeList _bombLevel[6];
+ int _currentLevel, _flashTime;
+ Hotspot *_vertexHotspot[25];
+ VertexType _lastVertex;
+ Notification _timerNotification;
+ NotificationCallBack _timerCallBack;
+
+ TimeValue _readTime;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp
new file mode 100644
index 0000000000..a3ce97d438
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp
@@ -0,0 +1,115 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/neighborhood/neighborhood.h"
+#include "pegasus/neighborhood/caldoria/caldoria.h"
+#include "pegasus/neighborhood/caldoria/caldoriamessages.h"
+
+namespace Pegasus {
+
+static const NotificationFlags kMessageDoneFlag = 1;
+
+CaldoriaMessages::CaldoriaMessages(Neighborhood *owner, const NotificationID id, NotificationManager *manager) :
+ GameInteraction(kCaldoriaMessagesInteractionID, owner), Notification(id, manager), _messageMovie(kCaldoriaMessagesID) {
+}
+
+void CaldoriaMessages::openInteraction() {
+ _neighborhoodNotification = GameInteraction::_owner->getNeighborhoodNotification();
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
+ _messageCallBack.setNotification(this);
+ notifyMe(this, kMessageDoneFlag, kMessageDoneFlag);
+ _messageCallBack.setCallBackFlag(kMessageDoneFlag);
+ _messageNumber = 1;
+}
+
+void CaldoriaMessages::initInteraction() {
+ GameInteraction::_owner->startExtraSequence(kCaBedroomVidPhone, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void CaldoriaMessages::closeInteraction() {
+ cancelNotification(this);
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void CaldoriaMessages::receiveNotification(Notification *notification, const NotificationFlags) {
+ if (notification == _neighborhoodNotification) {
+ switch (GameInteraction::_owner->getLastExtra()) {
+ case kCaBedroomVidPhone:
+ GameInteraction::_owner->showExtraView(kCaBedroomMessage1);
+ break;
+ case kCaBedroomMessage1:
+ play1Message(1);
+ break;
+ case kCaBedroomMessage2:
+ play1Message(2);
+ break;
+ }
+ } else {
+ _messageCallBack.releaseCallBack();
+ _messageMovie.releaseMovie();
+
+ uint32 extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2;
+ GameInteraction::_owner->showExtraView(extraID);
+ allowInput(true);
+ }
+}
+
+void CaldoriaMessages::clickInHotspot(const Input &input, const Hotspot *spot) {
+ uint32 extraID;
+
+ switch (spot->getObjectID()) {
+ case kCaBedroomVidPhoneActivationSpotID:
+ extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2;
+ GameInteraction::_owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ GameInteraction::clickInHotspot(input, spot);
+ break;
+ }
+}
+
+void CaldoriaMessages::play1Message(uint messageNumber) {
+ if (messageNumber == 1) {
+ _messageMovie.initFromMovieFile("Images/Caldoria/A12NVA.movie");
+ _messageNumber = 2;
+ } else {
+ _messageMovie.initFromMovieFile("Images/Caldoria/A12NVB.movie");
+ _messageNumber = 1;
+ GameState.setCaldoriaSeenMessages(true);
+ }
+
+ _messageMovie.moveElementTo(kCaldoriaMessageLeft, kCaldoriaMessageTop);
+ _messageMovie.setDisplayOrder(kCaldoriaMessagesOrder);
+ _messageMovie.startDisplaying();
+ _messageCallBack.initCallBack(&_messageMovie, kCallBackAtExtremes);
+ _messageCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ allowInput(false);
+ _messageMovie.show();
+ _messageMovie.redrawMovieWorld();
+ _messageMovie.start();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.h b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h
new file mode 100644
index 0000000000..955fe10ce9
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H
+#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H
+
+#include "pegasus/input.h"
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class Neighborhood;
+
+class CaldoriaMessages : public GameInteraction, public Notification, public NotificationReceiver {
+public:
+ CaldoriaMessages(Neighborhood *, const NotificationID, NotificationManager *);
+ virtual ~CaldoriaMessages() {}
+
+protected:
+ void openInteraction();
+ void initInteraction();
+ void closeInteraction();
+ void receiveNotification(Notification *, const NotificationFlags);
+ void clickInHotspot(const Input &, const Hotspot *);
+ void play1Message(uint);
+
+ Movie _messageMovie;
+ NotificationCallBack _messageCallBack;
+ Notification *_neighborhoodNotification;
+ uint _messageNumber;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp
new file mode 100644
index 0000000000..ff4d1811d0
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp
@@ -0,0 +1,135 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/neighborhood.h"
+#include "pegasus/neighborhood/caldoria/caldoria.h"
+#include "pegasus/neighborhood/caldoria/caldoriamirror.h"
+
+namespace Pegasus {
+
+CaldoriaMirror::CaldoriaMirror(Neighborhood *owner) : GameInteraction(kCaldoriaMirrorInteractionID, owner) {
+}
+
+void CaldoriaMirror::openInteraction() {
+ _neighborhoodNotification = _owner->getNeighborhoodNotification();
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
+}
+
+void CaldoriaMirror::initInteraction() {
+ _owner->setCurrentActivation(kActivateMirrorReady);
+ _owner->startExtraSequence(kCaBathroomGreeting, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void CaldoriaMirror::closeInteraction() {
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void CaldoriaMirror::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (_owner->getLastExtra() == (uint32)kCaBathroomAgencyStandard || !input.anyDirectionInput())
+ GameInteraction::handleInput(input, cursorSpot);
+}
+
+void CaldoriaMirror::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ switch (_owner->getLastExtra()) {
+ case kCaBathroomGreeting:
+ case kCaBathroomBodyFat:
+ case kCaBathroomRetrothrash:
+ case kCaBathroomGeoWave:
+ g_allHotspots.activateOneHotspot(kCaBathroomMirrorSpotID);
+ g_allHotspots.deactivateOneHotspot(kCaHairStyle1SpotID);
+ g_allHotspots.deactivateOneHotspot(kCaHairStyle2SpotID);
+ g_allHotspots.deactivateOneHotspot(kCaHairStyle3SpotID);
+ break;
+ case kCaBathroomStylistIntro:
+ case kCaBathroomRetrothrashReturn:
+ case kCaBathroomGeoWaveReturn:
+ g_allHotspots.activateOneHotspot(kCaHairStyle1SpotID);
+ g_allHotspots.activateOneHotspot(kCaHairStyle2SpotID);
+ g_allHotspots.activateOneHotspot(kCaHairStyle3SpotID);
+ g_allHotspots.deactivateOneHotspot(kCaBathroomMirrorSpotID);
+ break;
+ }
+}
+
+void CaldoriaMirror::clickInHotspot(const Input &input, const Hotspot *spot) {
+ switch (spot->getObjectID()) {
+ case kCaBathroomMirrorSpotID:
+ switch (_owner->getLastExtra()) {
+ case kCaBathroomGreeting:
+ _owner->startExtraSequence(kCaBathroomBodyFat, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaBathroomBodyFat:
+ _owner->startExtraSequence(kCaBathroomStylistIntro, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaBathroomRetrothrash:
+ _owner->startExtraSequence(kCaBathroomRetrothrashReturn, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaBathroomGeoWave:
+ _owner->startExtraSequence(kCaBathroomGeoWaveReturn, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ break;
+ case kCaHairStyle1SpotID:
+ _owner->startExtraSequence(kCaBathroomRetrothrash, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaHairStyle2SpotID:
+ _owner->startExtraSequence(kCaBathroomAgencyStandard, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCaHairStyle3SpotID:
+ _owner->startExtraSequence(kCaBathroomGeoWave, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ GameInteraction::clickInHotspot(input, spot);
+ break;
+ }
+}
+
+void CaldoriaMirror::receiveNotification(Notification *, const NotificationFlags) {
+ switch (_owner->getLastExtra()) {
+ case kCaBathroomRetrothrash:
+ case kCaBathroomGeoWave:
+ _owner->setCurrentActivation(kActivateMirrorReady);
+ break;
+ case kCaBathroomStylistIntro:
+ case kCaBathroomRetrothrashReturn:
+ case kCaBathroomGeoWaveReturn:
+ _owner->setCurrentActivation(kActivateStylistReady);
+ break;
+ case kCaBathroomAgencyStandard:
+ _owner->setCurrentActivation(kActivateHotSpotAlways);
+ _owner->requestDeleteCurrentInteraction();
+ GameState.setScoringFixedHair(true);
+ GameState.setCaldoriaDoneHygiene(true);
+ break;
+ }
+
+ allowInput(true);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.h b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h
new file mode 100644
index 0000000000..1ca47ec774
--- /dev/null
+++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H
+#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/notification.h"
+
+namespace Pegasus {
+
+class CaldoriaMirror : public GameInteraction, public NotificationReceiver {
+public:
+ CaldoriaMirror(Neighborhood *);
+ virtual ~CaldoriaMirror() {}
+
+protected:
+ void openInteraction();
+ void initInteraction();
+ void closeInteraction();
+
+ void handleInput(const Input &, const Hotspot *);
+ void activateHotspots();
+ void clickInHotspot(const Input &, const Hotspot *);
+ void receiveNotification(Notification *, const NotificationFlags);
+
+ Notification *_neighborhoodNotification;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/door.cpp b/engines/pegasus/neighborhood/door.cpp
new file mode 100644
index 0000000000..f7ec7559fc
--- /dev/null
+++ b/engines/pegasus/neighborhood/door.cpp
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "pegasus/neighborhood/door.h"
+
+namespace Pegasus {
+
+void DoorTable::loadFromStream(Common::SeekableReadStream *stream) {
+ uint32 count = stream->readUint32BE();
+ _entries.resize(count);
+
+ for (uint32 i = 0; i < count; i++) {
+ _entries[i].room = stream->readUint16BE();
+ _entries[i].direction = stream->readByte();
+ _entries[i].altCode = stream->readByte();
+ _entries[i].movieStart = stream->readUint32BE();
+ _entries[i].movieEnd = stream->readUint32BE();
+ _entries[i].flags = stream->readByte();
+ stream->readByte(); // alignment
+ debug(0, "Door[%d]: %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction,
+ _entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd,
+ _entries[i].flags);
+ }
+}
+
+void DoorTable::clear() {
+ _entries.clear();
+}
+
+DoorTable::Entry DoorTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) {
+ for (uint32 i = 0; i < _entries.size(); i++)
+ if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode)
+ return _entries[i];
+
+ return Entry();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/door.h b/engines/pegasus/neighborhood/door.h
new file mode 100644
index 0000000000..8ea757559a
--- /dev/null
+++ b/engines/pegasus/neighborhood/door.h
@@ -0,0 +1,90 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_DOOR_H
+#define PEGASUS_NEIGHBORHOOD_DOOR_H
+
+#include "common/array.h"
+#include "common/endian.h"
+
+#include "pegasus/constants.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+typedef byte DoorFlags;
+
+enum {
+ kDoorPresentBit, // Bit set if there is a door here.
+ kDoorLockedBit // Bit set if door is locked, clear if unlocked.
+};
+
+static const DoorFlags kNoDoorFlags = 0;
+static const DoorFlags kDoorPresentMask = 1 << kDoorPresentBit;
+static const DoorFlags kDoorLockedMask = 1 << kDoorLockedBit;
+
+class DoorTable {
+public:
+ DoorTable() {}
+ ~DoorTable() {}
+
+ static uint32 getResTag() { return MKTAG('D', 'o', 'o', 'r'); }
+
+ void loadFromStream(Common::SeekableReadStream *stream);
+ void clear();
+
+ struct Entry {
+ Entry() { clear(); }
+ bool isEmpty() { return movieStart == 0xffffffff; }
+ void clear() {
+ room = kNoRoomID;
+ direction = kNoDirection;
+ altCode = kNoAlternateID;
+ movieStart = 0xffffffff;
+ movieEnd = 0xffffffff;
+ flags = kNoDoorFlags;
+ }
+
+ RoomID room;
+ DirectionConstant direction;
+ AlternateID altCode;
+ TimeValue movieStart;
+ TimeValue movieEnd;
+ DoorFlags flags;
+ };
+
+ Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode);
+
+private:
+ Common::Array<Entry> _entries;
+};
+
+} // End of namespace Pegasus
+
+#endif
+
diff --git a/engines/pegasus/neighborhood/exit.cpp b/engines/pegasus/neighborhood/exit.cpp
new file mode 100644
index 0000000000..f0dfff12d3
--- /dev/null
+++ b/engines/pegasus/neighborhood/exit.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "pegasus/neighborhood/exit.h"
+
+namespace Pegasus {
+
+void ExitTable::loadFromStream(Common::SeekableReadStream *stream) {
+ uint32 count = stream->readUint32BE();
+ _entries.resize(count);
+
+ for (uint32 i = 0; i < count; i++) {
+ _entries[i].room = stream->readUint16BE();
+ _entries[i].direction = stream->readByte();
+ _entries[i].altCode = stream->readByte();
+ _entries[i].movieStart = stream->readUint32BE();
+ _entries[i].movieEnd = stream->readUint32BE();
+ _entries[i].exitEnd = stream->readUint32BE();
+ _entries[i].exitLoop = stream->readUint32BE();
+ _entries[i].exitRoom = stream->readUint16BE();
+ _entries[i].exitDirection = stream->readByte();
+ stream->readByte(); // alignment
+
+ _entries[i].originalEnd = _entries[i].exitEnd;
+
+ debug(0, "Exit[%d]: %d %d %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction,
+ _entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, _entries[i].exitEnd,
+ _entries[i].exitLoop, _entries[i].exitRoom, _entries[i].exitDirection);
+ }
+}
+
+void ExitTable::clear() {
+ _entries.clear();
+}
+
+ExitTable::Entry ExitTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) {
+ for (uint32 i = 0; i < _entries.size(); i++)
+ if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode)
+ return _entries[i];
+
+ return Entry();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/exit.h b/engines/pegasus/neighborhood/exit.h
new file mode 100644
index 0000000000..17150892f9
--- /dev/null
+++ b/engines/pegasus/neighborhood/exit.h
@@ -0,0 +1,93 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_EXIT_H
+#define PEGASUS_NEIGHBORHOOD_EXIT_H
+
+#include "common/array.h"
+#include "common/endian.h"
+
+#include "pegasus/constants.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+class ExitTable {
+public:
+ ExitTable() {}
+ ~ExitTable() {}
+
+ static uint32 getResTag() { return MKTAG('E', 'x', 'i', 't'); }
+
+ void loadFromStream(Common::SeekableReadStream *stream);
+ void clear();
+
+ struct Entry {
+ Entry() { clear(); }
+ bool isEmpty() { return movieStart == 0xffffffff; }
+ void clear() {
+ room = kNoRoomID;
+ direction = kNoDirection;
+ altCode = kNoAlternateID;
+ movieStart = 0xffffffff;
+ movieEnd = 0xffffffff;
+ exitEnd = 0xffffffff;
+ originalEnd = 0xffffffff;
+ exitLoop = 0xffffffff;
+ exitRoom = kNoRoomID;
+ exitDirection = kNoDirection;
+ }
+
+ RoomID room;
+ DirectionConstant direction;
+ AlternateID altCode;
+ TimeValue movieStart;
+ TimeValue movieEnd;
+ // exitEnd is the end of the optimized run of walks.
+ TimeValue exitEnd;
+ TimeValue originalEnd;
+ // exitLoop is the loop start time of the optimized run of walks if the run
+ // loops back on itself (so far, only in TSA).
+ TimeValue exitLoop;
+ RoomID exitRoom;
+ DirectionConstant exitDirection;
+ };
+
+ Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode);
+
+ typedef Common::Array<Entry>::iterator iterator;
+ iterator begin() { return _entries.begin(); }
+ iterator end() { return _entries.end(); }
+
+private:
+ Common::Array<Entry> _entries;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/extra.cpp b/engines/pegasus/neighborhood/extra.cpp
new file mode 100644
index 0000000000..b8c4e5b510
--- /dev/null
+++ b/engines/pegasus/neighborhood/extra.cpp
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "pegasus/neighborhood/extra.h"
+
+namespace Pegasus {
+
+void ExtraTable::loadFromStream(Common::SeekableReadStream *stream) {
+ uint32 count = stream->readUint32BE();
+ _entries.resize(count);
+
+ for (uint32 i = 0; i < count; i++) {
+ _entries[i].extra = stream->readUint32BE();
+ _entries[i].movieStart = stream->readUint32BE();
+ _entries[i].movieEnd = stream->readUint32BE();
+ debug(0, "Extra[%d]: %d %d %d", i, _entries[i].extra, _entries[i].movieStart, _entries[i].movieEnd);
+ }
+}
+
+void ExtraTable::clear() {
+ _entries.clear();
+}
+
+ExtraTable::Entry ExtraTable::findEntry(ExtraID extra) {
+ for (uint32 i = 0; i < _entries.size(); i++)
+ if (_entries[i].extra == extra)
+ return _entries[i];
+
+ return Entry();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/extra.h b/engines/pegasus/neighborhood/extra.h
new file mode 100644
index 0000000000..14fcff1009
--- /dev/null
+++ b/engines/pegasus/neighborhood/extra.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_EXTRA_H
+#define PEGASUS_NEIGHBORHOOD_EXTRA_H
+
+#include "common/array.h"
+#include "common/endian.h"
+
+#include "pegasus/constants.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+class ExtraTable {
+public:
+ ExtraTable() {}
+ ~ExtraTable() {}
+
+ static uint32 getResTag() { return MKTAG('X', 't', 'r', 'a'); }
+
+ void loadFromStream(Common::SeekableReadStream *stream);
+ void clear();
+
+ struct Entry {
+ Entry() { movieStart = 0xffffffff; }
+ bool isEmpty() { return movieStart == 0xffffffff; }
+
+ ExtraID extra;
+ TimeValue movieStart;
+ TimeValue movieEnd;
+ };
+
+ Entry findEntry(ExtraID extra);
+
+private:
+ Common::Array<Entry> _entries;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/hotspotinfo.cpp b/engines/pegasus/neighborhood/hotspotinfo.cpp
new file mode 100644
index 0000000000..c7524f3a0f
--- /dev/null
+++ b/engines/pegasus/neighborhood/hotspotinfo.cpp
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "pegasus/neighborhood/hotspotinfo.h"
+
+namespace Pegasus {
+
+void HotspotInfoTable::loadFromStream(Common::SeekableReadStream *stream) {
+ uint32 count = stream->readUint32BE();
+ _entries.resize(count);
+
+ for (uint32 i = 0; i < count; i++) {
+ _entries[i].hotspot = stream->readUint16BE();
+ _entries[i].hotspotActivation = stream->readSByte();
+ stream->readByte(); // alignment
+ _entries[i].hotspotRoom = stream->readUint16BE();
+ _entries[i].hotspotDirection = stream->readByte();
+ stream->readByte(); // alignment
+ _entries[i].hotspotExtra = stream->readUint32BE();
+ _entries[i].hotspotItem = stream->readUint16BE();
+ debug(0, "Hotspot[%d]: %d %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].hotspotActivation,
+ _entries[i].hotspotRoom, _entries[i].hotspotDirection, _entries[i].hotspotExtra,
+ _entries[i].hotspotItem);
+ }
+}
+
+void HotspotInfoTable::clear() {
+ _entries.clear();
+}
+
+HotspotInfoTable::Entry HotspotInfoTable::findEntry(HotSpotID hotspot) {
+ for (uint32 i = 0; i < _entries.size(); i++)
+ if (_entries[i].hotspot == hotspot)
+ return _entries[i];
+
+ return Entry();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/hotspotinfo.h b/engines/pegasus/neighborhood/hotspotinfo.h
new file mode 100644
index 0000000000..965f445ba8
--- /dev/null
+++ b/engines/pegasus/neighborhood/hotspotinfo.h
@@ -0,0 +1,77 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H
+#define PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H
+
+#include "common/array.h"
+#include "common/endian.h"
+
+#include "pegasus/constants.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+class HotspotInfoTable {
+public:
+ HotspotInfoTable() {}
+ ~HotspotInfoTable() {}
+
+ static uint32 getResTag() { return MKTAG('H', 'S', 'I', 'n'); }
+
+ void loadFromStream(Common::SeekableReadStream *stream);
+ void clear();
+
+ struct Entry {
+ Entry() { hotspotRoom = kNoRoomID; }
+ bool isEmpty() { return hotspotRoom == kNoRoomID; }
+
+ HotSpotID hotspot;
+ HotSpotActivationID hotspotActivation;
+ // Location hot spot lives in:
+ RoomID hotspotRoom;
+ DirectionConstant hotspotDirection;
+ // Extra to play if this is a "play extra" hot spot.
+ ExtraID hotspotExtra;
+ // Item corresponding to this hot spot if it is an item-related hot spot.
+ ItemID hotspotItem;
+ };
+
+ Entry findEntry(HotSpotID hotspot);
+
+ typedef Common::Array<Entry>::iterator iterator;
+ iterator begin() { return _entries.begin(); }
+ iterator end() { return _entries.end(); }
+
+private:
+ Common::Array<Entry> _entries;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/constants.h b/engines/pegasus/neighborhood/mars/constants.h
new file mode 100644
index 0000000000..82a7f03b68
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/constants.h
@@ -0,0 +1,941 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_CONSTANTS_H
+#define PEGASUS_NEIGHBORHOOD_MARS_CONSTANTS_H
+
+#include "pegasus/constants.h"
+
+namespace Pegasus {
+
+// Element Coordinates
+
+static const CoordType kUndoHiliteLeft = kNavAreaLeft + 140;
+static const CoordType kUndoHiliteTop = kNavAreaTop + 36;
+
+static const CoordType kCurrentGuessLeft = kNavAreaLeft + 146;
+static const CoordType kCurrentGuessTop = kNavAreaTop + 90;
+
+static const CoordType kReactorChoiceHiliteLeft = kNavAreaLeft + 116;
+static const CoordType kReactorChoiceHiliteTop = kNavAreaTop + 158;
+
+static const CoordType kReactorHistoryLeft = kNavAreaLeft + 302;
+static const CoordType kReactorHistoryTop = kNavAreaTop + 39;
+
+static const CoordType kAnswerLeft = kNavAreaLeft + 304;
+static const CoordType kAnswerTop = kNavAreaTop + 180;
+
+static const CoordType kShuttle1Left = 0;
+static const CoordType kShuttle1Top = 0;
+
+static const CoordType kShuttle2Left = 0;
+static const CoordType kShuttle2Top = 96;
+
+static const CoordType kShuttle3Left = 500;
+static const CoordType kShuttle3Top = 96;
+
+static const CoordType kShuttle4Left = 0;
+static const CoordType kShuttle4Top = 320;
+
+static const CoordType kShuttleWindowLeft = 140;
+static const CoordType kShuttleWindowTop = 96;
+static const CoordType kShuttleWindowWidth = 360;
+static const CoordType kShuttleWindowHeight = 224;
+
+static const CoordType kShuttleWindowMidH = (kShuttleWindowLeft * 2 + kShuttleWindowWidth) / 2;
+static const CoordType kShuttleWindowMidV = (kShuttleWindowTop * 2 + kShuttleWindowHeight) / 2;
+
+static const CoordType kShuttleLeftLeft = 0;
+static const CoordType kShuttleLeftTop = 128;
+
+static const CoordType kShuttleRightLeft = 506;
+static const CoordType kShuttleRightTop = 128;
+
+static const CoordType kShuttleLowerLeftLeft = 74;
+static const CoordType kShuttleLowerLeftTop = 358;
+
+static const CoordType kShuttleLowerRightLeft = 486;
+static const CoordType kShuttleLowerRightTop = 354;
+
+static const CoordType kShuttleCenterLeft = 260;
+static const CoordType kShuttleCenterTop = 336;
+
+static const CoordType kShuttleUpperLeftLeft = 30;
+static const CoordType kShuttleUpperLeftTop = 32;
+
+static const CoordType kShuttleUpperRightLeft = 506;
+static const CoordType kShuttleUpperRightTop = 52;
+
+static const CoordType kShuttleLeftEnergyLeft = 110;
+static const CoordType kShuttleLeftEnergyTop = 186;
+
+static const CoordType kShuttleRightEnergyLeft = 510;
+static const CoordType kShuttleRightEnergyTop = 186;
+
+static const CoordType kShuttleEnergyLeft = 186;
+static const CoordType kShuttleEnergyTop = 60;
+static const CoordType kShuttleEnergyWidth = 252;
+static const CoordType kShuttleEnergyHeight = 22;
+
+static const CoordType kPlanetStartLeft = kShuttleWindowLeft;
+static const CoordType kPlanetStartTop = kShuttleWindowTop + kShuttleWindowHeight;
+
+static const CoordType kPlanetStopLeft = kShuttleWindowLeft;
+static const CoordType kPlanetStopTop = kShuttleWindowTop + kShuttleWindowHeight - 100;
+
+static const CoordType kShuttleTractorLeft = kShuttleWindowLeft + 6;
+static const CoordType kShuttleTractorTop = kShuttleWindowTop + 56;
+static const CoordType kShuttleTractorWidth = 348;
+static const CoordType kShuttleTractorHeight = 112;
+
+static const CoordType kShuttleJunkLeft = kShuttleWindowLeft + 6;
+static const CoordType kShuttleJunkTop = kShuttleWindowTop + 6;
+
+static const DisplayOrder kShuttlePlanetOrder = kInterfaceLayer;
+static const DisplayOrder kShuttleAlienShipOrder = kShuttlePlanetOrder + 1;
+static const DisplayOrder kShuttleRobotShipOrder = kShuttleAlienShipOrder + 1;
+static const DisplayOrder kShuttleTractorBeamMovieOrder = kShuttleRobotShipOrder + 1;
+static const DisplayOrder kShuttleWeaponBackOrder = kShuttleTractorBeamMovieOrder + 1;
+static const DisplayOrder kShuttleJunkOrder = kShuttleWeaponBackOrder + 1;
+static const DisplayOrder kShuttleWeaponFrontOrder = kShuttleJunkOrder + 1;
+static const DisplayOrder kShuttleTractorBeamOrder = kShuttleWeaponFrontOrder + 1;
+static const DisplayOrder kShuttleHUDOrder = kShuttleTractorBeamOrder + 1;
+static const DisplayOrder kShuttleBackgroundOrder = kShuttleHUDOrder + 1;
+static const DisplayOrder kShuttleMonitorOrder = kShuttleBackgroundOrder + 1;
+static const DisplayOrder kShuttleStatusOrder = kShuttleMonitorOrder + 1;
+
+static const TimeValue kShuttleSwingStart = 0;
+static const TimeValue kShuttleSwingStop = 5 * 600;
+
+static const TimeValue kCanyonChaseStart = kShuttleSwingStop;
+static const TimeValue kCanyonChaseStop = 60 * 600 + 43 * 600 + 14 * 40;
+
+static const TimeValue kLaunchTubeReachedTime = 60 * 600 + 38 * 600 - kCanyonChaseStart;
+static const TimeValue kCanyonChaseFinishedTime = kCanyonChaseStop - kCanyonChaseStart -
+ kLaunchTubeReachedTime;
+
+// Left shuttle.
+
+static const TimeValue kShuttleLeftIntroStart = 0;
+static const TimeValue kShuttleLeftIntroStop = 400;
+
+static const TimeValue kShuttleLeftBlankTime = 400;
+
+static const TimeValue kShuttleLeftNormalTime = 440;
+
+static const TimeValue kShuttleLeftAutoTestTime = 480;
+
+static const TimeValue kShuttleLeftDamagedTime = 520;
+
+static const TimeValue kShuttleLeftDampingTime = 560;
+
+static const TimeValue kShuttleLeftGravitonTime = 600;
+
+static const TimeValue kShuttleLeftTractorTime = 640;
+
+// Right shuttle.
+
+static const TimeValue kShuttleRightIntroStart = 0;
+static const TimeValue kShuttleRightIntroStop = 400;
+
+static const TimeValue kShuttleRightDestroyedStart = 400;
+static const TimeValue kShuttleRightDestroyedStop = 840;
+
+static const TimeValue kShuttleRightBlankTime = 840;
+
+static const TimeValue kShuttleRightNormalTime = 880;
+
+static const TimeValue kShuttleRightDamagedTime = 920;
+
+static const TimeValue kShuttleRightTargetLockTime = 960;
+
+static const TimeValue kShuttleRightGravitonTime = 1000;
+
+static const TimeValue kShuttleRightOverloadTime = 1040;
+
+// Lower Left shuttle.
+
+static const TimeValue kShuttleLowerLeftCollisionTime = 0;
+
+static const TimeValue kShuttleLowerLeftTubeTime = 40;
+
+static const TimeValue kShuttleLowerLeftAutopilotTime = 80;
+
+// Lower Right shuttle.
+
+static const TimeValue kShuttleLowerRightOffTime = 0;
+
+static const TimeValue kShuttleLowerRightTrackingTime = 40;
+
+static const TimeValue kShuttleLowerRightTransportTime = 80;
+
+static const TimeValue kShuttleLowerRightTransportHiliteTime = 120;
+
+// Center shuttle.
+
+static const TimeValue kShuttleCenterBoardingTime = 0;
+
+static const TimeValue kShuttleCenterCheckTime = 40;
+
+static const TimeValue kShuttleCenterNavCompTime = 80;
+
+static const TimeValue kShuttleCenterCommTime = 120;
+
+static const TimeValue kShuttleCenterWeaponsTime = 160;
+
+static const TimeValue kShuttleCenterAllSystemsTime = 200;
+
+static const TimeValue kShuttleCenterSecureLooseTime = 240;
+
+static const TimeValue kShuttleCenterAutoTestTime = 280;
+
+static const TimeValue kShuttleCenterLaunchTime = 320;
+
+static const TimeValue kShuttleCenterEnterTubeTime = 360;
+
+static const TimeValue kShuttleCenterTargetSightedTime = 400;
+
+static const TimeValue kShuttleCenterVerifyingTime = 440;
+
+static const TimeValue kShuttleCenterScanningTime = 480;
+
+static const TimeValue kShuttleCenterSafeTime = 520;
+
+// Upper Left shuttle.
+
+static const TimeValue kShuttleUpperLeftDimTime = 0;
+
+static const TimeValue kShuttleUpperLeftDampingTime = 40;
+
+static const TimeValue kShuttleUpperLeftGravitonTime = 80;
+
+static const TimeValue kShuttleUpperLeftTractorTime = 120;
+
+// Upper Right shuttle.
+
+static const TimeValue kShuttleUpperRightLockedTime = 0;
+
+static const TimeValue kShuttleUpperRightArmedTime = 40;
+
+static const TimeValue kShuttleUpperRightAlienDestroyedTime = 80;
+
+static const TimeValue kShuttleUpperRightOverloadTime = 120;
+
+static const TimeValue kShuttleUpperRightTargetDestroyedTime = 160;
+
+// Shuttle distance
+
+static const int kShuttleDistance = 500;
+
+static const int kJunkMaxDistance = kShuttleDistance;
+static const int kJunkMinDistance = 40;
+
+static const int kEnergyBeamMaxDistance = kShuttleDistance;
+static const int kEnergyBeamMinDistance = 40;
+
+static const int kGravitonMaxDistance = kShuttleDistance;
+static const int kGravitonMinDistance = 40;
+
+static const TimeValue kMarsOxyMaskOnIn = 0;
+static const TimeValue kMarsOxyMaskOnOut = 1560;
+
+static const TimeValue kMarsAirlockButtonBeepIn = 1560;
+static const TimeValue kMarsAirlockButtonBeepOut = 1620;
+
+static const TimeValue kMarsColorMatchingButtonBeepIn = 1620;
+static const TimeValue kMarsColorMatchingButtonBeepOut = 1680;
+
+static const TimeValue kMarsKioskBeepIn = 1680;
+static const TimeValue kMarsKioskBeepOut = 1740;
+
+static const TimeValue kMarsBumpIntoWallIn = 1740;
+static const TimeValue kMarsBumpIntoWallOut = 1888;
+
+static const TimeValue kMarsGantryDoorCloseIn = 1888;
+static const TimeValue kMarsGantryDoorCloseOut = 2866;
+
+static const TimeValue kMarsTransportDoorCloseIn = 2866;
+static const TimeValue kMarsTransportDoorCloseOut = 3593;
+
+static const TimeValue kMarsAirlockPressurizeIn = 3593;
+static const TimeValue kMarsAirlockPressurizeOut = 4766;
+
+static const TimeValue kMarsBigAirlockDoorCloseIn = 4766;
+static const TimeValue kMarsBigAirlockDoorCloseOut = 7872;
+
+static const TimeValue kMarsSmallAirlockDoorCloseIn = 7872;
+static const TimeValue kMarsSmallAirlockDoorCloseOut = 10000;
+
+static const TimeValue kMarsMazeDoorCloseIn = 10000;
+static const TimeValue kMarsMazeDoorCloseOut = 10969;
+
+static const TimeValue kMarsRobotTakesTransportIn = 10969;
+static const TimeValue kMarsRobotTakesTransportOut = 12802;
+
+static const TimeValue kMarsPodDepartedUpperPlatformIn = 12802;
+static const TimeValue kMarsPodDepartedUpperPlatformOut = 15783;
+
+static const TimeValue kMarsPodDepartedLowerPlatformIn = 15783;
+static const TimeValue kMarsPodDepartedLowerPlatformOut = 18736;
+
+static const TimeValue kMarsPodArrivedUpperPlatformIn = 18736;
+static const TimeValue kMarsPodArrivedUpperPlatformOut = 21605;
+
+static const TimeValue kMarsCheckInRequiredIn = 21605;
+static const TimeValue kMarsCheckInRequiredOut = 27463;
+
+static const TimeValue kMarsCantOpenShuttleIn = 27463;
+static const TimeValue kMarsCantOpenShuttleOut = 29214;
+
+static const TimeValue kMarsShuttleLockOverrideIn = 29214;
+static const TimeValue kMarsShuttleLockOverrideOut = 30330;
+
+static const TimeValue kMarsNoShuttleIn = 30330;
+static const TimeValue kMarsNoShuttleOut = 31502;
+
+static const TimeValue kMustBeUnlockedIn = 31502;
+static const TimeValue kMustBeUnlockedOut = 33960;
+
+static const TimeValue kColorMatchBlueIn = 33960;
+static const TimeValue kColorMatchBlueOut = 34240;
+
+static const TimeValue kColorMatchRedIn = 34240;
+static const TimeValue kColorMatchRedOut = 34538;
+
+static const TimeValue kColorMatchGreenIn = 34538;
+static const TimeValue kColorMatchGreenOut = 34827;
+
+static const TimeValue kColorMatchYellowIn = 34827;
+static const TimeValue kColorMatchYellowOut = 35162;
+
+static const TimeValue kColorMatchPurpleIn = 35162;
+static const TimeValue kColorMatchPurpleOut = 35426;
+
+static const TimeValue kColorMatchZeroNodesIn = 35426;
+static const TimeValue kColorMatchZeroNodesOut = 36376;
+
+static const TimeValue kColorMatchOneNodeIn = 36376;
+static const TimeValue kColorMatchOneNodeOut = 37209;
+
+static const TimeValue kColorMatchTwoNodesIn = 37209;
+static const TimeValue kColorMatchTwoNodesOut = 37983;
+
+static const TimeValue kColorMatchThreeNodesIn = 37983;
+static const TimeValue kColorMatchThreeNodesOut = 38784;
+
+static const TimeValue kMarsShuttle1DepartedIn = 38784;
+static const TimeValue kMarsShuttle1DepartedOut = 40323;
+
+static const TimeValue kMarsShuttle2DepartedIn = 40323;
+static const TimeValue kMarsShuttle2DepartedOut = 41824;
+
+static const TimeValue kShuttleCockpitIn = 41824;
+static const TimeValue kShuttleCockpitOut = 43126;
+
+static const TimeValue kShuttleOnboardIn = 43126;
+static const TimeValue kShuttleOnboardOut = 44284;
+
+static const TimeValue kShuttleNavigationIn = 44284;
+static const TimeValue kShuttleNavigationOut = 46049;
+
+static const TimeValue kShuttleCommunicationIn = 46049;
+static const TimeValue kShuttleCommunicationOut = 47288;
+
+static const TimeValue kShuttleAutoTestingIn = 47288;
+static const TimeValue kShuttleAutoTestingOut = 48179;
+
+static const TimeValue kMarsThrusterAutoTestIn = 48179;
+static const TimeValue kMarsThrusterAutoTestOut = 49979;
+
+static const TimeValue kShuttleAllSystemsIn = 49979;
+static const TimeValue kShuttleAllSystemsOut = 51065;
+
+static const TimeValue kShuttleSecureLooseIn = 51065;
+static const TimeValue kShuttleSecureLooseOut = 52346;
+
+static const TimeValue kShuttlePrepareForDropIn = 52346;
+static const TimeValue kShuttlePrepareForDropOut = 53216;
+
+static const TimeValue kShuttleAllClearIn = 53216;
+static const TimeValue kShuttleAllClearOut = 54031;
+
+static const TimeValue kShuttleConfiguringIn = 54031;
+static const TimeValue kShuttleConfiguringOut = 54994;
+
+static const TimeValue kShuttleGeneratingIn = 54994;
+static const TimeValue kShuttleGeneratingOut = 56033;
+
+static const TimeValue kShuttleBreakawayIn = 56033;
+static const TimeValue kShuttleBreakawayOut = 57346;
+
+static const TimeValue kMarsAtmosphericBreakawayIn = 57346;
+static const TimeValue kMarsAtmosphericBreakawayOut = 59237;
+
+static const TimeValue kMarsCockpitChatterIn = 59237;
+static const TimeValue kMarsCockpitChatterOut = 70344;
+
+static const TimeValue kShuttleDamperDescIn = 70344;
+static const TimeValue kShuttleDamperDescOut = 73262;
+
+static const TimeValue kShuttleGravitonDescIn = 73262;
+static const TimeValue kShuttleGravitonDescOut = 75296;
+
+static const TimeValue kShuttleTractorDescIn = 75296;
+static const TimeValue kShuttleTractorDescOut = 78381;
+
+static const TimeValue kShuttleTargetSightedIn = 78381;
+static const TimeValue kShuttleTargetSightedOut = 79074;
+
+static const TimeValue kShuttleAutopilotEngagedIn = 79074;
+static const TimeValue kShuttleAutopilotEngagedOut = 80414;
+
+static const TimeValue kMarsEDBBlastIn = 80414;
+static const TimeValue kMarsEDBBlastOut = 80705;
+
+static const TimeValue kMarsGravitonBlastIn = 80705;
+static const TimeValue kMarsGravitonBlastOut = 81199;
+
+static const TimeValue kMarsJunkCollisionIn = 81199;
+static const TimeValue kMarsJunkCollisionOut = 81961;
+
+static const TimeValue kShuttleGravitonIn = 81961;
+static const TimeValue kShuttleGravitonOut = 82587;
+
+static const TimeValue kShuttleDampingBeamIn = 82587;
+static const TimeValue kShuttleDampingBeamOut = 83331;
+
+static const TimeValue kShuttleTractorBeamIn = 83331;
+static const TimeValue kShuttleTractorBeamOut = 83802;
+
+static const TimeValue kShuttleHullBreachIn = 83802;
+static const TimeValue kShuttleHullBreachOut = 84721;
+
+static const TimeValue kShuttleWingDamageIn = 84721;
+static const TimeValue kShuttleWingDamageOut = 85640;
+
+static const TimeValue kShuttleHullDamageIn = 85640;
+static const TimeValue kShuttleHullDamageOut = 86513;
+
+static const TimeValue kShuttleEnergyTooLowIn = 86513;
+static const TimeValue kShuttleEnergyTooLowOut = 87578;
+
+static const TimeValue kShuttleTractorLimitedIn = 87578;
+static const TimeValue kShuttleTractorLimitedOut = 89164;
+
+static const TimeValue kShuttleCantHoldIn = 89164;
+static const TimeValue kShuttleCantHoldOut = 90945;
+
+static const TimeValue kShuttleBrokeFreeIn = 90945;
+static const TimeValue kShuttleBrokeFreeOut = 92322;
+
+static const TimeValue kShuttleDestroyedIn = 92322;
+static const TimeValue kShuttleDestroyedOut = 93189;
+
+static const TimeValue kShuttleCoordinatesIn = 93189;
+static const TimeValue kShuttleCoordinatesOut = 94018;
+
+static const TimeValue kShuttleScanningIn = 94018;
+static const TimeValue kShuttleScanningOut = 94975;
+
+static const TimeValue kShuttleSafeIn = 94975;
+static const TimeValue kShuttleSafeOut = 96176;
+
+static const TimeValue kShuttleOverloadedIn = 96176;
+static const TimeValue kShuttleOverloadedOut = 101308;
+
+static const TimeScale kMarsMovieScale = 600;
+static const TimeScale kMarsFramesPerSecond = 15;
+static const TimeScale kMarsFrameDuration = 40;
+
+// Alternate IDs.
+
+static const AlternateID kAltMarsNormal = 0;
+static const AlternateID kAltMarsPodAtMars34 = 1;
+static const AlternateID kAltMarsTookCard = 2;
+static const AlternateID kAltMars35AirlockEast = 3;
+static const AlternateID kAltMars35AirlockWest = 4;
+static const AlternateID kAltMarsPodAtMars45 = 5;
+static const AlternateID kAltMarsTookMask = 6;
+static const AlternateID kAltMarsMaskOnFiller = 7;
+static const AlternateID kAltMars60AirlockEast = 8;
+static const AlternateID kAltMars60AirlockWest = 9;
+
+// Room IDs.
+
+static const RoomID kMars0A = 0;
+static const RoomID kMars00 = 1;
+static const RoomID kMars01 = 2;
+static const RoomID kMars02 = 3;
+static const RoomID kMars03 = 4;
+static const RoomID kMars04 = 5;
+static const RoomID kMars05 = 6;
+static const RoomID kMars06 = 7;
+static const RoomID kMars07 = 8;
+static const RoomID kMars08 = 9;
+static const RoomID kMars09 = 10;
+static const RoomID kMars10 = 11;
+static const RoomID kMars11 = 12;
+static const RoomID kMars12 = 13;
+static const RoomID kMars13 = 14;
+static const RoomID kMars14 = 15;
+static const RoomID kMars15 = 16;
+static const RoomID kMars16 = 17;
+static const RoomID kMars17 = 18;
+static const RoomID kMars18 = 19;
+static const RoomID kMars19 = 20;
+static const RoomID kMars20 = 21;
+static const RoomID kMars21 = 22;
+static const RoomID kMars22 = 23;
+static const RoomID kMars23 = 24;
+static const RoomID kMars24 = 25;
+static const RoomID kMars25 = 26;
+static const RoomID kMars26 = 27;
+static const RoomID kMars27 = 28;
+static const RoomID kMars28 = 29;
+static const RoomID kMars29 = 30;
+static const RoomID kMars30 = 31;
+static const RoomID kMars31 = 32;
+static const RoomID kMars31South = 33;
+static const RoomID kMars32 = 34;
+static const RoomID kMars33 = 35;
+static const RoomID kMars33North = 36;
+static const RoomID kMars34 = 37;
+static const RoomID kMars35 = 38;
+static const RoomID kMars36 = 39;
+static const RoomID kMars37 = 40;
+static const RoomID kMars38 = 41;
+static const RoomID kMars39 = 42;
+static const RoomID kMars41 = 43;
+static const RoomID kMars42 = 44;
+static const RoomID kMars43 = 45;
+static const RoomID kMars44 = 46;
+static const RoomID kMars45 = 47;
+static const RoomID kMars46 = 48;
+static const RoomID kMars47 = 49;
+static const RoomID kMars48 = 50;
+static const RoomID kMars49 = 51;
+static const RoomID kMars50 = 52;
+static const RoomID kMars51 = 53;
+static const RoomID kMars52 = 54;
+static const RoomID kMars54 = 55;
+static const RoomID kMars56 = 56;
+static const RoomID kMars58 = 57;
+static const RoomID kMars60 = 58;
+static const RoomID kMarsRobotShuttle = 59;
+static const RoomID kMarsMaze004 = 60;
+static const RoomID kMarsMaze005 = 61;
+static const RoomID kMarsMaze006 = 62;
+static const RoomID kMarsMaze007 = 63;
+static const RoomID kMarsMaze008 = 64;
+static const RoomID kMarsMaze009 = 65;
+static const RoomID kMarsMaze010 = 66;
+static const RoomID kMarsMaze011 = 67;
+static const RoomID kMarsMaze012 = 68;
+static const RoomID kMarsMaze015 = 69;
+static const RoomID kMarsMaze016 = 70;
+static const RoomID kMarsMaze017 = 71;
+static const RoomID kMarsMaze018 = 72;
+static const RoomID kMarsMaze019 = 73;
+static const RoomID kMarsMaze020 = 74;
+static const RoomID kMarsMaze021 = 75;
+static const RoomID kMarsMaze022 = 76;
+static const RoomID kMarsMaze023 = 77;
+static const RoomID kMarsMaze024 = 78;
+static const RoomID kMarsMaze025 = 79;
+static const RoomID kMarsMaze026 = 80;
+static const RoomID kMarsMaze027 = 81;
+static const RoomID kMarsMaze028 = 82;
+static const RoomID kMarsMaze031 = 83;
+static const RoomID kMarsMaze032 = 84;
+static const RoomID kMarsMaze033 = 85;
+static const RoomID kMarsMaze034 = 86;
+static const RoomID kMarsMaze035 = 87;
+static const RoomID kMarsMaze036 = 88;
+static const RoomID kMarsMaze037 = 89;
+static const RoomID kMarsMaze038 = 90;
+static const RoomID kMarsMaze039 = 91;
+static const RoomID kMarsMaze042 = 92;
+static const RoomID kMarsMaze043 = 93;
+static const RoomID kMarsMaze044 = 94;
+static const RoomID kMarsMaze045 = 95;
+static const RoomID kMarsMaze046 = 96;
+static const RoomID kMarsMaze047 = 97;
+static const RoomID kMarsMaze049 = 98;
+static const RoomID kMarsMaze050 = 99;
+static const RoomID kMarsMaze051 = 100;
+static const RoomID kMarsMaze052 = 101;
+static const RoomID kMarsMaze053 = 102;
+static const RoomID kMarsMaze054 = 103;
+static const RoomID kMarsMaze055 = 104;
+static const RoomID kMarsMaze056 = 105;
+static const RoomID kMarsMaze057 = 106;
+static const RoomID kMarsMaze058 = 107;
+static const RoomID kMarsMaze059 = 108;
+static const RoomID kMarsMaze060 = 109;
+static const RoomID kMarsMaze061 = 110;
+static const RoomID kMarsMaze063 = 111;
+static const RoomID kMarsMaze064 = 112;
+static const RoomID kMarsMaze065 = 113;
+static const RoomID kMarsMaze066 = 114;
+static const RoomID kMarsMaze067 = 115;
+static const RoomID kMarsMaze068 = 116;
+static const RoomID kMarsMaze069 = 117;
+static const RoomID kMarsMaze070 = 118;
+static const RoomID kMarsMaze071 = 119;
+static const RoomID kMarsMaze072 = 120;
+static const RoomID kMarsMaze074 = 121;
+static const RoomID kMarsMaze076 = 122;
+static const RoomID kMarsMaze078 = 123;
+static const RoomID kMarsMaze079 = 124;
+static const RoomID kMarsMaze081 = 125;
+static const RoomID kMarsMaze083 = 126;
+static const RoomID kMarsMaze084 = 127;
+static const RoomID kMarsMaze085 = 128;
+static const RoomID kMarsMaze086 = 129;
+static const RoomID kMarsMaze087 = 130;
+static const RoomID kMarsMaze088 = 131;
+static const RoomID kMarsMaze089 = 132;
+static const RoomID kMarsMaze090 = 133;
+static const RoomID kMarsMaze091 = 134;
+static const RoomID kMarsMaze092 = 135;
+static const RoomID kMarsMaze093 = 136;
+static const RoomID kMarsMaze098 = 137;
+static const RoomID kMarsMaze099 = 138;
+static const RoomID kMarsMaze100 = 139;
+static const RoomID kMarsMaze101 = 140;
+static const RoomID kMarsMaze104 = 141;
+static const RoomID kMarsMaze105 = 142;
+static const RoomID kMarsMaze106 = 143;
+static const RoomID kMarsMaze107 = 144;
+static const RoomID kMarsMaze108 = 145;
+static const RoomID kMarsMaze111 = 146;
+static const RoomID kMarsMaze113 = 147;
+static const RoomID kMarsMaze114 = 148;
+static const RoomID kMarsMaze115 = 149;
+static const RoomID kMarsMaze116 = 150;
+static const RoomID kMarsMaze117 = 151;
+static const RoomID kMarsMaze118 = 152;
+static const RoomID kMarsMaze119 = 153;
+static const RoomID kMarsMaze120 = 154;
+static const RoomID kMarsMaze121 = 155;
+static const RoomID kMarsMaze122 = 156;
+static const RoomID kMarsMaze123 = 157;
+static const RoomID kMarsMaze124 = 158;
+static const RoomID kMarsMaze125 = 159;
+static const RoomID kMarsMaze126 = 160;
+static const RoomID kMarsMaze127 = 161;
+static const RoomID kMarsMaze128 = 162;
+static const RoomID kMarsMaze129 = 163;
+static const RoomID kMarsMaze130 = 164;
+static const RoomID kMarsMaze131 = 165;
+static const RoomID kMarsMaze132 = 166;
+static const RoomID kMarsMaze133 = 167;
+static const RoomID kMarsMaze136 = 168;
+static const RoomID kMarsMaze137 = 169;
+static const RoomID kMarsMaze138 = 170;
+static const RoomID kMarsMaze139 = 171;
+static const RoomID kMarsMaze140 = 172;
+static const RoomID kMarsMaze141 = 173;
+static const RoomID kMarsMaze142 = 174;
+static const RoomID kMarsMaze143 = 175;
+static const RoomID kMarsMaze144 = 176;
+static const RoomID kMarsMaze145 = 177;
+static const RoomID kMarsMaze146 = 178;
+static const RoomID kMarsMaze147 = 179;
+static const RoomID kMarsMaze148 = 180;
+static const RoomID kMarsMaze149 = 181;
+static const RoomID kMarsMaze152 = 182;
+static const RoomID kMarsMaze153 = 183;
+static const RoomID kMarsMaze154 = 184;
+static const RoomID kMarsMaze155 = 185;
+static const RoomID kMarsMaze156 = 186;
+static const RoomID kMarsMaze157 = 187;
+static const RoomID kMarsMaze159 = 188;
+static const RoomID kMarsMaze160 = 189;
+static const RoomID kMarsMaze161 = 190;
+static const RoomID kMarsMaze162 = 191;
+static const RoomID kMarsMaze163 = 192;
+static const RoomID kMarsMaze164 = 193;
+static const RoomID kMarsMaze165 = 194;
+static const RoomID kMarsMaze166 = 195;
+static const RoomID kMarsMaze167 = 196;
+static const RoomID kMarsMaze168 = 197;
+static const RoomID kMarsMaze169 = 198;
+static const RoomID kMarsMaze170 = 199;
+static const RoomID kMarsMaze171 = 200;
+static const RoomID kMarsMaze172 = 201;
+static const RoomID kMarsMaze173 = 202;
+static const RoomID kMarsMaze174 = 203;
+static const RoomID kMarsMaze175 = 204;
+static const RoomID kMarsMaze177 = 205;
+static const RoomID kMarsMaze178 = 206;
+static const RoomID kMarsMaze179 = 207;
+static const RoomID kMarsMaze180 = 208;
+static const RoomID kMarsMaze181 = 209;
+static const RoomID kMarsMaze182 = 210;
+static const RoomID kMarsMaze183 = 211;
+static const RoomID kMarsMaze184 = 212;
+static const RoomID kMarsMaze187 = 213;
+static const RoomID kMarsMaze188 = 214;
+static const RoomID kMarsMaze189 = 215;
+static const RoomID kMarsMaze190 = 216;
+static const RoomID kMarsMaze191 = 217;
+static const RoomID kMarsMaze192 = 218;
+static const RoomID kMarsMaze193 = 219;
+static const RoomID kMarsMaze194 = 220;
+static const RoomID kMarsMaze195 = 221;
+static const RoomID kMarsMaze198 = 222;
+static const RoomID kMarsMaze199 = 223;
+static const RoomID kMarsMaze200 = 224;
+static const RoomID kMarsDeathRoom = 225;
+
+// Hot Spot Activation IDs.
+
+static const HotSpotActivationID kActivationReadyForKiosk = 1;
+static const HotSpotActivationID kActivationKioskChoice = 2;
+static const HotSpotActivationID kActivationTunnelMapReady = 3;
+static const HotSpotActivationID kActivateMarsPodClosed = 4;
+static const HotSpotActivationID kActivateMarsPodOpen = 5;
+static const HotSpotActivationID kActivateReadyToPressurizeAirlock = 6;
+static const HotSpotActivationID kActivateAirlockPressurized = 7;
+static const HotSpotActivationID kActivateMaskOnHolder = 8;
+static const HotSpotActivationID kActivateMaskOnFiller = 9;
+static const HotSpotActivationID kActivateReactorPlatformOut = 10;
+static const HotSpotActivationID kActivateReactorPlatformIn = 11;
+static const HotSpotActivationID kActivateReactorAskLowerScreen = 12;
+static const HotSpotActivationID kActivateReactorReadyForNitrogen = 13;
+static const HotSpotActivationID kActivateReactorReadyForCrowBar = 14;
+static const HotSpotActivationID kActivateReactorAskOperation = 15;
+static const HotSpotActivationID kActivateReactorRanEvaluation = 16;
+static const HotSpotActivationID kActivateReactorRanDiagnostics = 17;
+static const HotSpotActivationID kActivateReactorAnalyzed = 18;
+static const HotSpotActivationID kActivateReactorInstructions = 19;
+static const HotSpotActivationID kActivateReactorInGame = 20;
+static const HotSpotActivationID kActivateReactorBombSafe = 21;
+static const HotSpotActivationID kActivateReactorBombExposed = 22;
+static const HotSpotActivationID kActivationRobotHeadClosed = 23;
+static const HotSpotActivationID kActivationRobotHeadOpen = 24;
+
+// Hot Spot IDs.
+
+static const HotSpotID kMars11NorthKioskSpotID = 5000;
+static const HotSpotID kMars11NorthKioskSightsSpotID = 5001;
+static const HotSpotID kMars11NorthKioskColonySpotID = 5002;
+static const HotSpotID kMars12NorthKioskSpotID = 5003;
+static const HotSpotID kMars12NorthKioskSightsSpotID = 5004;
+static const HotSpotID kMars12NorthKioskColonySpotID = 5005;
+static const HotSpotID kMars31SouthSpotID = 5006;
+static const HotSpotID kMars31SouthOutSpotID = 5007;
+static const HotSpotID kMars31SouthCardSpotID = 5008;
+static const HotSpotID kMars33NorthSpotID = 5009;
+static const HotSpotID kMars33NorthOutSpotID = 5010;
+static const HotSpotID kMars33NorthMonitorSpotID = 5011;
+static const HotSpotID kMars34NorthCardDropSpotID = 5012;
+static const HotSpotID kMars34SouthOpenStorageSpotID = 5013;
+static const HotSpotID kMars34SouthCloseStorageSpotID = 5014;
+static const HotSpotID kMars34SouthCrowbarSpotID = 5015;
+static const HotSpotID kMars35EastPressurizeSpotID = 5016;
+static const HotSpotID kMars35EastSpinSpotID = 5017;
+static const HotSpotID kMars35WestPressurizeSpotID = 5018;
+static const HotSpotID kMars35WestSpinSpotID = 5019;
+static const HotSpotID kMars45NorthOpenStorageSpotID = 5020;
+static const HotSpotID kMars45NorthCloseStorageSpotID = 5021;
+static const HotSpotID kMars45NorthCrowbarSpotID = 5022;
+static const HotSpotID kAttackRobotHotSpotID = 5023;
+static const HotSpotID kMars49AirMaskSpotID = 5024;
+static const HotSpotID kMars49AirMaskFilledSpotID = 5025;
+static const HotSpotID kMars49AirFillingDropSpotID = 5026;
+static const HotSpotID kMars52MoveLeftSpotID = 5027;
+static const HotSpotID kMars52MoveRightSpotID = 5028;
+static const HotSpotID kMars52ExtractSpotID = 5029;
+static const HotSpotID kMars53RetractSpotID = 5030;
+static const HotSpotID kMars54MoveLeftSpotID = 5031;
+static const HotSpotID kMars54MoveRightSpotID = 5032;
+static const HotSpotID kMars54ExtractSpotID = 5033;
+static const HotSpotID kMars55RetractSpotID = 5034;
+static const HotSpotID kMars56MoveLeftSpotID = 5035;
+static const HotSpotID kMars56MoveRightSpotID = 5036;
+static const HotSpotID kMars56ExtractSpotID = 5037;
+static const HotSpotID kMars57RetractSpotID = 5038;
+static const HotSpotID kMars57LowerScreenSpotID = 5039;
+static const HotSpotID kMars57Retract2SpotID = 5040;
+static const HotSpotID kMars57DropNitrogenSpotID = 5041;
+static const HotSpotID kMars57DropCrowBarSpotID = 5042;
+static const HotSpotID kMars57CantOpenPanelSpotID = 5043;
+static const HotSpotID kMars57ShieldEvaluationSpotID = 5044;
+static const HotSpotID kMars57MeasureOutputSpotID = 5045;
+static const HotSpotID kMars57RunDiagnosticsSpotID = 5046;
+static const HotSpotID kMars57BackToOperationMenuSpotID = 5047;
+static const HotSpotID kMars57AnalyzeObjectSpotID = 5048;
+static const HotSpotID kMars57RemoveObjectMenuSpotID = 5049;
+static const HotSpotID kMars57CircuitLinkSpotID = 5050;
+static const HotSpotID kMars57CancelCircuitLinkSpotID = 5051;
+static const HotSpotID kMars57GameInstructionsSpotID = 5052;
+static const HotSpotID kMars57UndoMoveSpotID = 5053;
+static const HotSpotID kMars57RedMoveSpotID = 5054;
+static const HotSpotID kMars57YellowMoveSpotID = 5055;
+static const HotSpotID kMars57GreenMoveSpotID = 5056;
+static const HotSpotID kMars57BlueMoveSpotID = 5057;
+static const HotSpotID kMars57PurpleMoveSpotID = 5058;
+static const HotSpotID kMars57LowerScreenSafelySpotID = 5059;
+static const HotSpotID kMars57GrabBombSpotID = 5060;
+static const HotSpotID kMars58MoveLeftSpotID = 5061;
+static const HotSpotID kMars58MoveRightSpotID = 5062;
+static const HotSpotID kMars58ExtractSpotID = 5063;
+static const HotSpotID kMars59RetractSpotID = 5064;
+static const HotSpotID kMars60EastPressurizeSpotID = 5065;
+static const HotSpotID kMars60EastSpinSpotID = 5066;
+static const HotSpotID kMars60WestPressurizeSpotID = 5067;
+static const HotSpotID kMars60WestSpinSpotID = 5068;
+static const HotSpotID kRobotShuttleOpenHeadSpotID = 5069;
+static const HotSpotID kRobotShuttleMapChipSpotID = 5070;
+static const HotSpotID kRobotShuttleOpticalChipSpotID = 5071;
+static const HotSpotID kRobotShuttleShieldChipSpotID = 5072;
+
+// Extra sequence IDs.
+
+static const ExtraID kMarsArrivalFromTSA = 0;
+static const ExtraID kMars0AWatchShuttleDepart = 1;
+static const ExtraID kRobotThrowsPlayer = 2;
+static const ExtraID kMarsInfoKioskIntro = 3;
+static const ExtraID kMarsColonyInfo = 4;
+static const ExtraID kMarsSightsInfo = 5;
+static const ExtraID kRobotOnWayToShuttle = 6;
+static const ExtraID kMars31SouthZoomInNoCard = 7;
+static const ExtraID kMars31SouthViewNoCard = 8;
+static const ExtraID kMars31SouthZoomOutNoCard = 9;
+static const ExtraID kMars31SouthZoomViewNoCard = 10;
+static const ExtraID kMars33SlideShow1 = 11;
+static const ExtraID kMars33SlideShow2 = 12;
+static const ExtraID kMars33SlideShow3 = 13;
+static const ExtraID kMars33SlideShow4 = 14;
+static const ExtraID kMars34SpotOpenWithBar = 15;
+static const ExtraID kMars34SpotCloseWithBar = 16;
+static const ExtraID kMars34SpotOpenNoBar = 17;
+static const ExtraID kMars34SpotCloseNoBar = 18;
+static const ExtraID kMars34ViewOpenWithBar = 19;
+static const ExtraID kMars34ViewOpenNoBar = 20;
+static const ExtraID kMars34NorthPodGreeting = 21;
+static const ExtraID kMarsTurnOnPod = 22;
+static const ExtraID kMarsTakePodToMars45 = 23;
+static const ExtraID kMars35WestSpinAirlockToEast = 24;
+static const ExtraID kMars35EastSpinAirlockToWest = 25;
+static const ExtraID kMars45SpotOpenWithBar = 26;
+static const ExtraID kMars45SpotCloseWithBar = 27;
+static const ExtraID kMars45SpotOpenNoBar = 28;
+static const ExtraID kMars45SpotCloseNoBar = 29;
+static const ExtraID kMars45ViewOpenWithBar = 30;
+static const ExtraID kMars45ViewOpenNoBar = 31;
+static const ExtraID kMars48RobotApproaches = 32;
+static const ExtraID kMars48RobotKillsPlayer = 33;
+static const ExtraID kMars48RobotLoops = 34;
+static const ExtraID kMars48RobotView = 35;
+static const ExtraID kMars48RobotDefends = 36;
+static const ExtraID kMars49SouthViewMaskFilling = 37;
+static const ExtraID kMars52SpinLeft = 38;
+static const ExtraID kMars52SpinRight = 39;
+static const ExtraID kMars52Extend = 40;
+static const ExtraID kMars53Retract = 41;
+static const ExtraID kMars54SpinLeft = 42;
+static const ExtraID kMars54SpinRight = 43;
+static const ExtraID kMars54Extend = 44;
+static const ExtraID kMars55Retract = 45;
+static const ExtraID kMars56SpinLeft = 46;
+static const ExtraID kMars56SpinRight = 47;
+static const ExtraID kMars56ExtendWithBomb = 48;
+static const ExtraID kMars56ExtendNoBomb = 49;
+static const ExtraID kMars57RetractWithBomb = 50;
+static const ExtraID kMars57RetractNoBomb = 51;
+static const ExtraID kMars57LowerScreenClosed = 52;
+static const ExtraID kMars57CantOpenPanel = 53;
+static const ExtraID kMars57FreezeLock = 54;
+static const ExtraID kMars57BreakLock = 55;
+static const ExtraID kMars57LockFrozenView = 56;
+static const ExtraID kMars57ThawLock = 57;
+static const ExtraID kMars57OpenPanel = 58;
+static const ExtraID kMars57OpenPanelChoices = 59;
+static const ExtraID kMars57ShieldEvaluation = 60;
+static const ExtraID kMars57MeasureOutput = 61;
+static const ExtraID kMars57ShieldOkayLoop = 62;
+static const ExtraID kMars57RunDiagnostics = 63;
+static const ExtraID kMars57BombExplodes = 64;
+static const ExtraID kMars57BombAnalysis = 65;
+static const ExtraID kMars57DontLink = 66;
+static const ExtraID kMars57CircuitLink = 67;
+static const ExtraID kMars57GameLevel1 = 68;
+static const ExtraID kMars57GameLevel2 = 69;
+static const ExtraID kMars57GameLevel3 = 70;
+static const ExtraID kMars57BombExplodesInGame = 71;
+static const ExtraID kMars57GameSolved = 72;
+static const ExtraID kMars57ExposeBomb = 73;
+static const ExtraID kMars57BackToNormal = 74;
+static const ExtraID kMars57ViewOpenNoBomb = 75;
+static const ExtraID kMars58SpinLeft = 76;
+static const ExtraID kMars58SpinRight = 77;
+static const ExtraID kMars58Extend = 78;
+static const ExtraID kMars59Retract = 79;
+static const ExtraID kMars60WestSpinAirlockToEast = 80;
+static const ExtraID kMars60EastSpinAirlockToWest = 81;
+static const ExtraID kMarsRobotHeadOpen = 82;
+static const ExtraID kMarsRobotHeadClose = 83;
+static const ExtraID kMarsRobotHead000 = 84;
+static const ExtraID kMarsRobotHead001 = 85;
+static const ExtraID kMarsRobotHead010 = 86;
+static const ExtraID kMarsRobotHead011 = 87;
+static const ExtraID kMarsRobotHead100 = 88;
+static const ExtraID kMarsRobotHead101 = 89;
+static const ExtraID kMarsRobotHead110 = 90;
+static const ExtraID kMarsRobotHead111 = 91;
+static const ExtraID kMarsMaze007RobotApproach = 92;
+static const ExtraID kMarsMaze007RobotLoop = 93;
+static const ExtraID kMarsMaze007RobotDeath = 94;
+static const ExtraID kMarsMaze015SouthRobotApproach = 95;
+static const ExtraID kMarsMaze015SouthRobotLoop = 96;
+static const ExtraID kMarsMaze015SouthRobotDeath = 97;
+static const ExtraID kMarsMaze101EastRobotApproach = 98;
+static const ExtraID kMarsMaze101EastRobotLoop = 99;
+static const ExtraID kMarsMaze101EastRobotDeath = 100;
+static const ExtraID kMarsMaze104WestLoop = 101;
+static const ExtraID kMarsMaze104WestDeath = 102;
+static const ExtraID kMarsMaze133SouthApproach = 103;
+static const ExtraID kMarsMaze133SouthLoop = 104;
+static const ExtraID kMarsMaze133SouthDeath = 105;
+static const ExtraID kMarsMaze136NorthApproach = 106;
+static const ExtraID kMarsMaze136NorthLoop = 107;
+static const ExtraID kMarsMaze136NorthDeath = 108;
+static const ExtraID kMarsMaze184WestLoop = 109;
+static const ExtraID kMarsMaze184WestDeath = 110;
+static const ExtraID kMars200DeathInBucket = 111;
+
+static const ResIDType kReactorUndoHilitePICTID = 900;
+
+static const int16 kMars52Compass = 90;
+static const int16 kMars54Compass = 180;
+static const int16 kMars56Compass = 270;
+static const int16 kMars58Compass = 0;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/energybeam.cpp b/engines/pegasus/neighborhood/mars/energybeam.cpp
new file mode 100644
index 0000000000..964c8ba381
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/energybeam.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/mars/energybeam.h"
+
+namespace Pegasus {
+
+static const TimeValue kEnergyBeamTime = kOneSecond * kShuttleWeaponScale / 2;
+
+static const CoordType kEnergyBeamOriginH = kShuttleWindowMidH;
+static const CoordType kEnergyBeamOriginV = kShuttleWindowTop + kShuttleWindowHeight;
+
+static const float kBeamXOrigin = convertScreenHToSpaceX(kEnergyBeamOriginH, kEnergyBeamMinDistance);
+static const float kBeamYOrigin = convertScreenVToSpaceY(kEnergyBeamOriginV, kEnergyBeamMinDistance);
+static const float kBeamZOrigin = kEnergyBeamMinDistance;
+
+EnergyBeam::EnergyBeam() {
+ _weaponDuration = kEnergyBeamTime;
+ setSegment(0, kEnergyBeamTime);
+ _weaponOrigin = Point3D(kBeamXOrigin, kBeamYOrigin, kBeamZOrigin);
+}
+
+void EnergyBeam::draw(const Common::Rect &) {
+ static const int kBeamColorRed1 = 224;
+ static const int kBeamColorRed2 = 64;
+
+ Graphics::Surface *surface = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
+
+ byte red = linearInterp(0, kEnergyBeamTime, _lastTime, kBeamColorRed1, kBeamColorRed2);
+ uint32 color = surface->format.RGBToColor(red, 0, 0);
+
+ Point3D startPoint;
+ if (_weaponTime < 0.1)
+ startPoint = _weaponOrigin;
+ else
+ linearInterp(_weaponOrigin, _weaponTarget, _weaponTime - 0.1, startPoint);
+
+ Common::Point lineStart;
+ project3DTo2D(startPoint, lineStart);
+
+ Common::Point lineEnd;
+ project3DTo2D(_weaponLocation, lineEnd);
+
+ surface->drawThickLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, 2, 1, color);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/energybeam.h b/engines/pegasus/neighborhood/mars/energybeam.h
new file mode 100644
index 0000000000..715ed4b01d
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/energybeam.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_ENERGYBEAM_H
+#define PEGASUS_NEIGHBORHOOD_MARS_ENERGYBEAM_H
+
+#include "pegasus/neighborhood/mars/shuttleweapon.h"
+
+namespace Pegasus {
+
+class EnergyBeam : public ShuttleWeapon {
+public:
+ EnergyBeam();
+ virtual ~EnergyBeam() {}
+
+ void draw(const Common::Rect &);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/gravitoncannon.cpp b/engines/pegasus/neighborhood/mars/gravitoncannon.cpp
new file mode 100644
index 0000000000..d04b3d08b2
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/gravitoncannon.cpp
@@ -0,0 +1,134 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/neighborhood/mars/gravitoncannon.h"
+#include "pegasus/neighborhood/mars/robotship.h"
+#include "pegasus/neighborhood/mars/spacejunk.h"
+
+namespace Pegasus {
+
+static const TimeValue kGravitonTime = kOneSecond * kShuttleWeaponScale;
+
+static const CoordType kGravitonOriginH = kShuttleWindowLeft - 1;
+static const CoordType kGravitonOriginV = kShuttleWindowMidV;
+
+static const float kGravitonXOrigin = convertScreenHToSpaceX(kGravitonOriginH, kGravitonMinDistance);
+static const float kGravitonYOrigin = convertScreenVToSpaceY(kGravitonOriginV, kGravitonMinDistance);
+static const float kGravitonZOrigin = kGravitonMinDistance;
+
+// Width of graviton sprite...
+static const CoordType kGravitonMaxScreenWidth = 78;
+static const CoordType kGravitonMaxScreenHeight = 46;
+
+static const float kGravitonWidth = convertScreenHToSpaceX(kShuttleWindowMidH + kGravitonMaxScreenWidth / 2, kGravitonMinDistance)
+ - convertScreenHToSpaceX(kShuttleWindowMidH - kGravitonMaxScreenWidth / 2, kGravitonMinDistance);
+static const float kGravitonHeight = convertScreenVToSpaceY(kShuttleWindowMidV - kGravitonMaxScreenHeight / 2, kGravitonMinDistance)
+ - convertScreenVToSpaceY(kShuttleWindowMidV + kGravitonMaxScreenHeight / 2, kGravitonMinDistance);
+
+GravitonCannon::GravitonCannon() {
+ _weaponDuration = kGravitonTime;
+ setSegment(0, kGravitonTime);
+ _weaponOrigin = Point3D(kGravitonXOrigin, kGravitonYOrigin, kGravitonZOrigin);
+ _rightOrigin = Point3D(-kGravitonXOrigin, kGravitonYOrigin, kGravitonZOrigin);
+}
+
+void GravitonCannon::initShuttleWeapon() {
+ ShuttleWeapon::initShuttleWeapon();
+ _gravitonImage.getImageFromPICTFile("Images/Mars/Graviton Cannon");
+ _gravitonImage.getSurfaceBounds(_gravitonBounds);
+}
+
+void GravitonCannon::cleanUpShuttleWeapon() {
+ _gravitonImage.deallocateSurface();
+ ShuttleWeapon::cleanUpShuttleWeapon();
+}
+
+void GravitonCannon::draw(const Common::Rect &) {
+ // Left graviton...
+ Point3D pt3D = _weaponLocation;
+ pt3D.translate(-kGravitonWidth / 2, kGravitonHeight / 2, 0);
+ Common::Point pt2D;
+ project3DTo2D(pt3D, pt2D);
+ Common::Rect gravitonRect;
+ gravitonRect.left = pt2D.x;
+ gravitonRect.top = pt2D.y;
+
+ pt3D.translate(kGravitonWidth, -kGravitonHeight, 0);
+ project3DTo2D(pt3D, pt2D);
+ gravitonRect.right = pt2D.x;
+ gravitonRect.bottom = pt2D.y;
+
+ _gravitonImage.scaleTransparentCopy(_gravitonBounds, gravitonRect);
+
+ // Right graviton...
+ pt3D = _rightLocation;
+ pt3D.translate(-kGravitonWidth / 2, kGravitonHeight / 2, 0);
+ project3DTo2D(pt3D, pt2D);
+ gravitonRect.left = pt2D.x;
+ gravitonRect.top = pt2D.y;
+
+ pt3D.translate(kGravitonWidth, -kGravitonHeight, 0);
+ project3DTo2D(pt3D, pt2D);
+ gravitonRect.right = pt2D.x;
+ gravitonRect.bottom = pt2D.y;
+
+ _gravitonImage.scaleTransparentCopy(_gravitonBounds, gravitonRect);
+}
+
+void GravitonCannon::updateWeaponPosition() {
+ ShuttleWeapon::updateWeaponPosition();
+ if (_weaponTime != 1.0)
+ linearInterp(_rightOrigin, _weaponTarget, _weaponTime, _rightLocation);
+}
+
+bool GravitonCannon::collisionWithJunk(Common::Point &impactPoint) {
+ if (getDisplayOrder() == kShuttleWeaponFrontOrder) {
+ Point3D junkPosition;
+ g_spaceJunk->getJunkPosition(junkPosition);
+
+ if (junkPosition.z < _weaponLocation.z) {
+ setDisplayOrder(kShuttleWeaponBackOrder);
+ project3DTo2D(_weaponLocation, impactPoint);
+
+ if (g_spaceJunk->pointInJunk(impactPoint))
+ return true;
+
+ project3DTo2D(_rightLocation, impactPoint);
+ return g_spaceJunk->pointInJunk(impactPoint);
+ }
+ }
+
+ return false;
+}
+
+void GravitonCannon::hitJunk(Common::Point impactPoint) {
+ g_spaceJunk->hitByGravitonCannon(impactPoint);
+}
+
+void GravitonCannon::hitShuttle(Common::Point impactPoint) {
+ g_robotShip->hitByGravitonCannon(impactPoint);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/gravitoncannon.h b/engines/pegasus/neighborhood/mars/gravitoncannon.h
new file mode 100644
index 0000000000..b94fd55e5b
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/gravitoncannon.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_GRAVITONCANNON_H
+#define PEGASUS_NEIGHBORHOOD_MARS_GRAVITONCANNON_H
+
+#include "pegasus/surface.h"
+#include "pegasus/neighborhood/mars/shuttleweapon.h"
+
+namespace Pegasus {
+
+class GravitonCannon : public ShuttleWeapon {
+public:
+ GravitonCannon();
+ virtual ~GravitonCannon() {}
+
+ void initShuttleWeapon();
+ void cleanUpShuttleWeapon();
+
+ void draw(const Common::Rect &);
+
+protected:
+ virtual void updateWeaponPosition();
+ virtual bool collisionWithJunk(Common::Point &impactPoint);
+ virtual void hitJunk(Common::Point impactPoint);
+ virtual void hitShuttle(Common::Point impactPoint);
+
+ Surface _gravitonImage;
+ Common::Rect _gravitonBounds;
+ Point3D _rightOrigin, _rightLocation;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/hermite.cpp b/engines/pegasus/neighborhood/mars/hermite.cpp
new file mode 100644
index 0000000000..7f631b369d
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/hermite.cpp
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/neighborhood/mars/hermite.h"
+
+namespace Pegasus {
+
+CoordType hermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 time, int32 duration) {
+ float t = (float)time / duration;
+ float tsq = t * t;
+ float tcu = t * tsq;
+ float tcu2 = tcu + tcu;
+ float tsq2 = tsq + tsq;
+ float tsq3 = tsq2 + tsq;
+ return (CoordType)((tcu2 - tsq3 + 1) * p1 + (tsq3 - tcu2) * p4 + (tcu - tsq2 + t) * r1 + (tcu - tsq) * r4);
+}
+
+CoordType dHermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 time, int32 duration) {
+ float t = (float)time / duration;
+ float t2 = t + t;
+ float t4 = t2 + t2;
+ float t6 = t4 + t2;
+ float tsq = t * t;
+ float tsq3 = tsq + tsq + tsq;
+ float tsq6 = tsq3 + tsq3;
+ return (CoordType)((tsq6 - t6) * p1 + (t6 - tsq6) * p4 + (tsq3 - t4 + 1) * r1 + (tsq3 - t2) * r4);
+}
+
+void hermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 time, int32 duration, Common::Point &result) {
+ float t = (float)time / duration;
+ float tsq = t * t;
+ float tcu = t * tsq;
+ float tcu2 = tcu + tcu;
+ float tsq2 = tsq + tsq;
+ float tsq3 = tsq2 + tsq;
+
+ result.x = (int16)((tcu2 - tsq3 + 1) * p1.x + (tsq3 - tcu2) * p4.x + (tcu - tsq2 + t) * r1.x + (tcu - tsq) * r4.x);
+ result.y = (int16)((tcu2 - tsq3 + 1) * p1.y + (tsq3 - tcu2) * p4.y + (tcu - tsq2 + t) * r1.y + (tcu - tsq) * r4.y);
+}
+
+void dHermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 time, int32 duration, Common::Point &result) {
+ float t = (float)time / duration;
+ float t2 = t + t;
+ float t4 = t2 + t2;
+ float t6 = t4 + t2;
+ float tsq = t * t;
+ float tsq3 = tsq + tsq + tsq;
+ float tsq6 = tsq3 + tsq3;
+
+ result.x = (int16)((tsq6 - t6) * p1.x + (t6 - tsq6) * p4.x + (tsq3 - t4 + 1) * r1.x + (tsq3 - t2) * r4.x);
+ result.y = (int16)((tsq6 - t6) * p1.y + (t6 - tsq6) * p4.y + (tsq3 - t4 + 1) * r1.y + (tsq3 - t2) * r4.y);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/hermite.h b/engines/pegasus/neighborhood/mars/hermite.h
new file mode 100644
index 0000000000..44cb3a5a11
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/hermite.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_HERMITE_H
+#define PEGASUS_NEIGHBORHOOD_MARS_HERMITE_H
+
+#include "common/rect.h"
+#include "pegasus/types.h"
+
+namespace Pegasus {
+
+CoordType hermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 t, int32 duration);
+CoordType dHermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 t, int32 duration);
+void hermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 t, int32 duration, Common::Point &result);
+void dHermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 t, int32 duration, Common::Point &result);
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/mars.cpp b/engines/pegasus/neighborhood/mars/mars.cpp
new file mode 100644
index 0000000000..34c9e3d0f8
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/mars.cpp
@@ -0,0 +1,3735 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/events.h"
+#include "video/qt_decoder.h"
+
+#include "pegasus/cursor.h"
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/opticalchip.h"
+#include "pegasus/items/biochips/shieldchip.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/mars/mars.h"
+
+namespace Pegasus {
+
+// This should really be 22.5.
+// Probably no one will know the difference.
+static const int16 kMarsShieldPanelOffsetAngle = 22;
+
+static const CanMoveForwardReason kCantMoveRobotBlocking = kCantMoveLastReason + 1;
+
+static const NotificationFlags kTimeForCanyonChaseFlag = kLastNeighborhoodNotificationFlag << 1;
+static const NotificationFlags kExplosionFinishedFlag = kTimeForCanyonChaseFlag << 1;
+static const NotificationFlags kTimeToTransportFlag = kExplosionFinishedFlag << 1;
+
+static const NotificationFlags kMarsNotificationFlags = kTimeForCanyonChaseFlag |
+ kExplosionFinishedFlag |
+ kTimeToTransportFlag;
+
+static const TimeValue kLittleExplosionStart = 0 * 40;
+static const TimeValue kLittleExplosionStop = 24 * 40;
+
+static const TimeValue kBigExplosionStart = 24 * 40;
+static const TimeValue kBigExplosionStop = 62 * 40;
+
+enum {
+ kMaze007RobotLoopingEvent,
+ kMaze015RobotLoopingEvent,
+ kMaze101RobotLoopingEvent,
+ kMaze104RobotLoopingEvent,
+ kMaze133RobotLoopingEvent,
+ kMaze136RobotLoopingEvent,
+ kMaze184RobotLoopingEvent
+};
+
+enum {
+ kMaze007RobotLoopingTime = (64 + 96) * kMarsFrameDuration,
+ kMaze015RobotLoopingTime = (64 + 93) * kMarsFrameDuration,
+ kMaze101RobotLoopingTime = (64 + 45) * kMarsFrameDuration,
+ kMaze104RobotLoopingTime = 96 * kMarsFrameDuration,
+ kMaze133RobotLoopingTime = (64 + 96) * kMarsFrameDuration,
+ kMaze136RobotLoopingTime = (64 + 96) * kMarsFrameDuration,
+ kMaze184RobotLoopingTime = 96 * kMarsFrameDuration
+};
+
+// I've made a couple macros for these rects so we don't
+// have to globally construct them or whatnot
+#define kShuttleEnergyBeamBounds Common::Rect(24, 27, 24 + 112, 27 + 46)
+#define kShuttleGravitonBounds Common::Rect(24, 73, 24 + 112, 73 + 30)
+#define kShuttleTractorBounds Common::Rect(24, 103, 24 + 112, 103 + 30)
+#define kShuttleTransportBounds Common::Rect(484, 353, 89 + 484, 79 + 353)
+
+void MarsTimerEvent::fire() {
+ mars->marsTimerExpired(*this);
+}
+
+Mars::Mars(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Mars", kMarsID),
+ _guessObject(kNoDisplayElement), _undoPict(kNoDisplayElement), _guessHistory(kNoDisplayElement),
+ _choiceHighlight(kNoDisplayElement), _shuttleInterface1(kNoDisplayElement), _shuttleInterface2(kNoDisplayElement),
+ _shuttleInterface3(kNoDisplayElement), _shuttleInterface4(kNoDisplayElement), _canyonChaseMovie(kNoDisplayElement),
+ _leftShuttleMovie(kNoDisplayElement), _rightShuttleMovie(kNoDisplayElement), _lowerLeftShuttleMovie(kNoDisplayElement),
+ _lowerRightShuttleMovie(kNoDisplayElement), _centerShuttleMovie(kNoDisplayElement),
+ _upperLeftShuttleMovie(kNoDisplayElement), _upperRightShuttleMovie(kNoDisplayElement),
+ _leftDamageShuttleMovie(kNoDisplayElement), _rightDamageShuttleMovie(kNoDisplayElement), _explosions(kNoDisplayElement),
+ _planetMovie(kNoDisplayElement), _junk(kNoDisplayElement), _energyChoiceSpot(kShuttleEnergySpotID),
+ _gravitonChoiceSpot(kShuttleGravitonSpotID), _tractorChoiceSpot(kShuttleTractorSpotID),
+ _shuttleViewSpot(kShuttleViewSpotID), _shuttleTransportSpot(kShuttleTransportSpotID) {
+ _noAirFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::airStageExpired));
+ setIsItemTaken(kMarsCard);
+ setIsItemTaken(kAirMask);
+ setIsItemTaken(kCrowbar);
+ setIsItemTaken(kCardBomb);
+}
+
+Mars::~Mars() {
+ _vm->getAllHotspots().remove(&_energyChoiceSpot);
+ _vm->getAllHotspots().remove(&_gravitonChoiceSpot);
+ _vm->getAllHotspots().remove(&_tractorChoiceSpot);
+ _vm->getAllHotspots().remove(&_shuttleViewSpot);
+ _vm->getAllHotspots().remove(&_shuttleTransportSpot);
+}
+
+void Mars::init() {
+ Neighborhood::init();
+
+ Hotspot *attackSpot = _vm->getAllHotspots().findHotspotByID(kAttackRobotHotSpotID);
+ attackSpot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag);
+ _attackingItem = NULL;
+
+ forceStridingStop(kMars08, kNorth, kAltMarsNormal);
+
+ _neighborhoodNotification.notifyMe(this, kMarsNotificationFlags, kMarsNotificationFlags);
+
+ _explosionCallBack.setNotification(&_neighborhoodNotification);
+ _explosionCallBack.setCallBackFlag(kExplosionFinishedFlag);
+
+ _weaponSelection = kNoWeapon;
+}
+
+void Mars::flushGameState() {
+ g_energyMonitor->saveCurrentEnergyValue();
+}
+
+void Mars::start() {
+ g_energyMonitor->stopEnergyDraining();
+ g_energyMonitor->restoreLastEnergyValue();
+ _vm->resetEnergyDeathReason();
+ g_energyMonitor->startEnergyDraining();
+ Neighborhood::start();
+}
+
+class AirMaskCondition : public AICondition {
+public:
+ AirMaskCondition(const uint32);
+
+ virtual bool fireCondition();
+
+protected:
+ uint32 _airThreshold;
+ uint32 _lastAirLevel;
+};
+
+AirMaskCondition::AirMaskCondition(const uint32 airThreshold) {
+ _airThreshold = airThreshold;
+ _lastAirLevel = g_airMask->getAirLeft();
+}
+
+bool AirMaskCondition::fireCondition() {
+ bool result = g_airMask && g_airMask->isAirMaskOn() &&
+ g_airMask->getAirLeft() <= _airThreshold && _lastAirLevel > _airThreshold;
+
+ _lastAirLevel = g_airMask->getAirLeft();
+ return result;
+}
+
+void Mars::setUpAIRules() {
+ Neighborhood::setUpAIRules();
+
+ // Don't add these rules if we're going to the robot's shuttle...
+ if (g_AIArea && !GameState.getMarsReadyForShuttleTransport()) {
+ AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB1E", false);
+ AILocationCondition *locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kMars47, kSouth));
+ AIRule *rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Mars/XM27NB", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kMars27, kNorth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Mars/XM27NB", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kMars28, kNorth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Mars/XM41ED", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kMars19, kEast));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kMars35, kWest));
+ rule = new AIRule(locCondition, deactivate);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Mars/XM41ED", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kMars48, kWest));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ AirMaskCondition *airMask50Condition = new AirMaskCondition(50);
+ messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB1", false);
+ AIRule *rule50 = new AIRule(airMask50Condition, messageAction);
+
+ AirMaskCondition *airMask25Condition = new AirMaskCondition(25);
+ AICompoundAction *compound = new AICompoundAction();
+ messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB2", false);
+ compound->addAction(messageAction);
+ deactivate = new AIDeactivateRuleAction(rule50);
+ compound->addAction(deactivate);
+ AIRule *rule25 = new AIRule(airMask25Condition, compound);
+
+ AirMaskCondition *airMask5Condition = new AirMaskCondition(5);
+ compound = new AICompoundAction;
+ messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB3", false);
+ compound->addAction(messageAction);
+ deactivate = new AIDeactivateRuleAction(rule50);
+ compound->addAction(deactivate);
+ deactivate = new AIDeactivateRuleAction(rule25);
+ compound->addAction(deactivate);
+ AIRule *rule5 = new AIRule(airMask5Condition, compound);
+
+ g_AIArea->addAIRule(rule5);
+ g_AIArea->addAIRule(rule25);
+ g_AIArea->addAIRule(rule50);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Mars/XM51ND", false);
+ AIDoorOpenedCondition *doorOpen = new AIDoorOpenedCondition(MakeRoomView(kMars51, kEast));
+ rule = new AIRule(doorOpen, messageAction);
+ g_AIArea->addAIRule(rule);
+ }
+}
+
+uint16 Mars::getDateResID() const {
+ return kDate2185ID;
+}
+
+TimeValue Mars::getViewTime(const RoomID room, const DirectionConstant direction) {
+ ExtraTable::Entry extra;
+ SpotTable::Entry spotEntry;
+ uint32 extraID = 0xffffffff;
+
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kMars0A, kNorth):
+ if (!GameState.getMarsSeenTimeStream()) {
+ getExtraEntry(kMarsArrivalFromTSA, extra);
+ return extra.movieStart;
+ }
+ break;
+ case MakeRoomView(kMars31South, kSouth):
+ if (GameState.isTakenItemID(kMarsCard))
+ extraID = kMars31SouthZoomViewNoCard;
+ break;
+ case MakeRoomView(kMars31, kSouth):
+ if (GameState.isTakenItemID(kMarsCard))
+ extraID = kMars31SouthViewNoCard;
+ break;
+ case MakeRoomView(kMars34, kSouth):
+ if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) {
+ if (GameState.isTakenItemID(kCrowbar))
+ extraID = kMars34ViewOpenNoBar;
+ else
+ extraID = kMars34ViewOpenWithBar;
+ }
+ break;
+ case MakeRoomView(kMars36, kSouth):
+ case MakeRoomView(kMars37, kSouth):
+ case MakeRoomView(kMars38, kSouth):
+ findSpotEntry(room, direction, kSpotOnTurnMask | kSpotLoopsMask, spotEntry);
+ return spotEntry.movieStart;
+ case MakeRoomView(kMars45, kNorth):
+ if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) {
+ if (GameState.isTakenItemID(kCrowbar))
+ extraID = kMars45ViewOpenNoBar;
+ else
+ extraID = kMars45ViewOpenWithBar;
+ }
+ break;
+ case MakeRoomView(kMars48, kEast):
+ if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot())
+ extraID = kMars48RobotView;
+ break;
+ case MakeRoomView(kMars56, kEast):
+ if (_privateFlags.getFlag(kMarsPrivateBombExposedFlag)) {
+ if (_privateFlags.getFlag(kMarsPrivateDraggingBombFlag))
+ extraID = kMars57ViewOpenNoBomb;
+ else
+ extraID = kMars57ExposeBomb;
+ } else if (GameState.getMarsLockBroken()) {
+ extraID = kMars57OpenPanelChoices;
+ } else if (GameState.getMarsLockFrozen()) {
+ extraID = kMars57LockFrozenView;
+ }
+ break;
+ case MakeRoomView(kMarsRobotShuttle, kEast):
+ if (getCurrentActivation() == kActivationRobotHeadOpen) {
+ extraID = kMarsRobotHead111;
+
+ if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag))
+ extraID -= 1;
+ if (_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag))
+ extraID -= 2;
+ if (_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag))
+ extraID -= 4;
+ }
+ break;
+ }
+
+ if (extraID == 0xffffffff)
+ return Neighborhood::getViewTime(room, direction);
+
+ getExtraEntry(extraID, extra);
+ return extra.movieEnd - 1;
+}
+
+void Mars::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) {
+ Neighborhood::getZoomEntry(spotID, entry);
+
+ uint32 extraID = 0xffffffff;
+
+ switch (spotID) {
+ case kMars31SouthSpotID:
+ if (GameState.getCurrentDirection() == kSouth && GameState.isTakenItemID(kMarsCard))
+ extraID = kMars31SouthZoomInNoCard;
+ break;
+ case kMars31SouthOutSpotID:
+ if (GameState.getCurrentDirection() == kSouth && GameState.isTakenItemID(kMarsCard))
+ extraID = kMars31SouthZoomOutNoCard;
+ break;
+ }
+
+ if (extraID != 0xffffffff) {
+ ExtraTable::Entry extra;
+ getExtraEntry(extraID, extra);
+ entry.movieStart = extra.movieStart;
+ entry.movieEnd = extra.movieEnd;
+ }
+}
+
+void Mars::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) {
+ Neighborhood::findSpotEntry(room, direction, flags, entry);
+
+ if ((flags & (kSpotOnArrivalMask | kSpotOnTurnMask)) != 0) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars27, kNorth):
+ if (GameState.getMarsSeenThermalScan())
+ entry.clear();
+ else
+ GameState.setMarsSeenThermalScan(true);
+ break;
+ case MakeRoomView(kMars28, kNorth):
+ if (GameState.getMarsSeenThermalScan())
+ entry.clear();
+ else
+ GameState.setMarsSeenThermalScan(true);
+ break;
+ }
+ }
+}
+
+CanMoveForwardReason Mars::canMoveForward(ExitTable::Entry &entry) {
+ CanMoveForwardReason reason = Neighborhood::canMoveForward(entry);
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars48, kEast):
+ if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot())
+ reason = kCantMoveRobotBlocking;
+ break;
+ case MakeRoomView(kMars48, kSouth):
+ if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot())
+ _utilityFuse.stopFuse();
+ break;
+ }
+
+ return reason;
+}
+
+void Mars::cantMoveThatWay(CanMoveForwardReason reason) {
+ if (reason == kCantMoveRobotBlocking) {
+ startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput);
+ loadLoopSound2("");
+ } else {
+ Neighborhood::cantMoveThatWay(reason);
+ }
+}
+
+void Mars::moveForward() {
+ if (GameState.getCurrentRoom() == kMars02 || (GameState.getCurrentRoom() >= kMars05 && GameState.getCurrentRoom() <= kMars08))
+ loadLoopSound2("");
+
+ Neighborhood::moveForward();
+}
+
+void Mars::bumpIntoWall() {
+ requestSpotSound(kMarsBumpIntoWallIn, kMarsBumpIntoWallOut, kFilterNoInput, 0);
+ Neighborhood::bumpIntoWall();
+}
+
+CanOpenDoorReason Mars::canOpenDoor(DoorTable::Entry &entry) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars05, kEast):
+ case MakeRoomView(kMars06, kEast):
+ case MakeRoomView(kMars07, kEast):
+ if (!GameState.getMarsSecurityDown())
+ return kCantOpenLocked;
+ break;
+ case MakeRoomView(kMarsMaze037, kWest):
+ case MakeRoomView(kMarsMaze038, kEast):
+ if (GameState.getMarsMazeDoorPair1())
+ return kCantOpenLocked;
+ break;
+ case MakeRoomView(kMarsMaze050, kNorth):
+ case MakeRoomView(kMarsMaze058, kSouth):
+ if (!GameState.getMarsMazeDoorPair1())
+ return kCantOpenLocked;
+ break;
+ case MakeRoomView(kMarsMaze047, kNorth):
+ case MakeRoomView(kMarsMaze142, kSouth):
+ if (GameState.getMarsMazeDoorPair2())
+ return kCantOpenLocked;
+ break;
+ case MakeRoomView(kMarsMaze057, kNorth):
+ case MakeRoomView(kMarsMaze136, kSouth):
+ if (!GameState.getMarsMazeDoorPair2())
+ return kCantOpenLocked;
+ break;
+ case MakeRoomView(kMarsMaze120, kWest):
+ case MakeRoomView(kMarsMaze121, kEast):
+ if (GameState.getMarsMazeDoorPair3())
+ return kCantOpenLocked;
+ break;
+ case MakeRoomView(kMarsMaze081, kNorth):
+ case MakeRoomView(kMarsMaze083, kSouth):
+ if (!GameState.getMarsMazeDoorPair3())
+ return kCantOpenLocked;
+ break;
+ }
+
+ return Neighborhood::canOpenDoor(entry);
+}
+
+void Mars::cantOpenDoor(CanOpenDoorReason reason) {
+ switch (GameState.getCurrentRoom()) {
+ case kMars05:
+ case kMars06:
+ case kMars07:
+ playSpotSoundSync(kMarsCantOpenShuttleIn, kMarsCantOpenShuttleOut);
+ break;
+ default:
+ Neighborhood::cantOpenDoor(reason);
+ break;
+ }
+}
+
+void Mars::openDoor() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars06, kEast):
+ case MakeRoomView(kMars07, kEast):
+ if (GameState.getMarsSecurityDown())
+ playSpotSoundSync(kMarsNoShuttleIn, kMarsNoShuttleOut);
+ break;
+ case MakeRoomView(kMars47, kSouth):
+ if (GameState.isTakenItemID(kAirMask))
+ setCurrentAlternate(kAltMarsTookMask);
+ else
+ setCurrentAlternate(kAltMarsNormal);
+ break;
+ case MakeRoomView(kMars48, kNorth):
+ if (GameState.getMarsPodAtUpperPlatform())
+ setCurrentAlternate(kAltMarsNormal);
+ else
+ setCurrentAlternate(kAltMarsPodAtMars45);
+ break;
+ case MakeRoomView(kMars48, kEast):
+ if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) {
+ die(kDeathDidntGetOutOfWay);
+ return;
+ }
+ break;
+ }
+
+ Neighborhood::openDoor();
+}
+
+void Mars::doorOpened() {
+ switch (GameState.getCurrentRoom()) {
+ case kMars27:
+ case kMars28:
+ if (GameState.getCurrentDirection() == kNorth)
+ _vm->die(kDeathArrestedInMars);
+ else
+ Neighborhood::doorOpened();
+ break;
+ case kMars41:
+ case kMars42:
+ if (GameState.getCurrentDirection() == kEast)
+ _vm->die(kDeathWrongShuttleLock);
+ else
+ Neighborhood::doorOpened();
+ break;
+ case kMars51:
+ Neighborhood::doorOpened();
+ setUpReactorEnergyDrain();
+
+ if (g_AIArea)
+ g_AIArea->checkRules();
+ break;
+ case kMars19:
+ if (GameState.getCurrentDirection() == kEast)
+ GameState.setMarsAirlockOpen(true);
+
+ Neighborhood::doorOpened();
+ break;
+ case kMars48:
+ if (GameState.getCurrentDirection() == kWest)
+ GameState.setMarsAirlockOpen(true);
+
+ Neighborhood::doorOpened();
+ break;
+ default:
+ Neighborhood::doorOpened();
+ break;
+ }
+}
+
+void Mars::setUpReactorEnergyDrain() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars51, kEast):
+ if (GameState.isCurrentDoorOpen()) {
+ if (g_energyMonitor->getEnergyDrainRate() == kEnergyDrainNormal) {
+ if (GameState.getShieldOn()) {
+ g_shield->setItemState(kShieldRadiation);
+ g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainWithShield);
+ } else {
+ g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainNoShield);
+ }
+ _vm->setEnergyDeathReason(kDeathReactorBurn);
+ }
+ } else {
+ if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) {
+ if (GameState.getShieldOn())
+ g_shield->setItemState(kShieldNormal);
+ g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal);
+ _vm->resetEnergyDeathReason();
+ }
+ }
+ break;
+ case MakeRoomView(kMars52, kNorth):
+ case MakeRoomView(kMars52, kSouth):
+ case MakeRoomView(kMars52, kEast):
+ case MakeRoomView(kMars52, kWest):
+ case MakeRoomView(kMars54, kNorth):
+ case MakeRoomView(kMars54, kSouth):
+ case MakeRoomView(kMars54, kEast):
+ case MakeRoomView(kMars54, kWest):
+ case MakeRoomView(kMars56, kNorth):
+ case MakeRoomView(kMars56, kSouth):
+ case MakeRoomView(kMars56, kEast):
+ case MakeRoomView(kMars56, kWest):
+ case MakeRoomView(kMars58, kNorth):
+ case MakeRoomView(kMars58, kSouth):
+ case MakeRoomView(kMars58, kEast):
+ case MakeRoomView(kMars58, kWest):
+ if (g_energyMonitor->getEnergyDrainRate() == kEnergyDrainNormal) {
+ if (GameState.getShieldOn()) {
+ g_shield->setItemState(kShieldRadiation);
+ g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainWithShield);
+ } else {
+ g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainNoShield);
+ }
+ _vm->setEnergyDeathReason(kDeathReactorBurn);
+ }
+ break;
+ default:
+ if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) {
+ if (GameState.getShieldOn())
+ g_shield->setItemState(kShieldNormal);
+ g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal);
+ _vm->resetEnergyDeathReason();
+ }
+ break;
+ }
+}
+
+void Mars::closeDoorOffScreen(const RoomID room, const DirectionConstant direction) {
+ switch (room) {
+ case kMars51:
+ playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut);
+ if (GameState.getShieldOn())
+ g_shield->setItemState(kShieldNormal);
+ g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal);
+ _vm->resetEnergyDeathReason();
+ break;
+ case kMars05:
+ case kMars06:
+ case kMars07:
+ case kMars13:
+ case kMars22:
+ case kMars47:
+ case kMars52:
+ playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut);
+ break;
+ case kMars18:
+ case kMars32:
+ playSpotSoundSync(kMarsTransportDoorCloseIn, kMarsTransportDoorCloseOut);
+ break;
+ case kMars19:
+ if (GameState.getCurrentRoom() != kMars35) {
+ playSpotSoundSync(kMarsBigAirlockDoorCloseIn, kMarsBigAirlockDoorCloseOut);
+ GameState.setMarsAirlockOpen(false);
+ }
+ break;
+ case kMars36:
+ if (GameState.getCurrentRoom() != kMars35)
+ playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut);
+ break;
+ case kMars48:
+ if (direction == kWest) {
+ if (GameState.getCurrentRoom() != kMars60) {
+ playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut);
+ GameState.setMarsAirlockOpen(false);
+ }
+ } else {
+ playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut);
+ }
+ break;
+ case kMars41:
+ case kMars42:
+ case kMars43:
+ if (direction == kWest)
+ playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut);
+ break;
+ case kMarsMaze037:
+ case kMarsMaze038:
+ case kMarsMaze012:
+ case kMarsMaze066:
+ case kMarsMaze050:
+ case kMarsMaze058:
+ case kMarsMaze057:
+ case kMarsMaze136:
+ case kMarsMaze047:
+ case kMarsMaze142:
+ case kMarsMaze133:
+ case kMarsMaze132:
+ case kMarsMaze113:
+ case kMarsMaze114:
+ case kMarsMaze120:
+ case kMarsMaze121:
+ case kMarsMaze081:
+ case kMarsMaze083:
+ case kMarsMaze088:
+ case kMarsMaze089:
+ case kMarsMaze179:
+ case kMarsMaze180:
+ playSpotSoundSync(kMarsMazeDoorCloseIn, kMarsMazeDoorCloseOut);
+ break;
+ }
+}
+
+void Mars::checkAirlockDoors() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars19, kWest):
+ case MakeRoomView(kMars18, kWest):
+ case MakeRoomView(kMars17, kWest):
+ case MakeRoomView(kMars16, kWest):
+ case MakeRoomView(kMars15, kWest):
+ case MakeRoomView(kMars14, kWest):
+ case MakeRoomView(kMars12, kWest):
+ case MakeRoomView(kMars11, kWest):
+ case MakeRoomView(kMars10, kWest):
+ if (GameState.getMarsInAirlock()) {
+ playSpotSoundSync(kMarsBigAirlockDoorCloseIn, kMarsBigAirlockDoorCloseOut);
+ GameState.setMarsInAirlock(false);
+ }
+ break;
+ case MakeRoomView(kMars36, kEast):
+ case MakeRoomView(kMars37, kEast):
+ case MakeRoomView(kMars38, kEast):
+ case MakeRoomView(kMars39, kEast):
+ case MakeRoomView(kMars48, kEast):
+ case MakeRoomView(kMars50, kEast):
+ case MakeRoomView(kMars51, kEast):
+ case MakeRoomView(kMars52, kEast):
+ if (GameState.getMarsInAirlock()) {
+ playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut);
+ GameState.setMarsInAirlock(false);
+ }
+ break;
+ case MakeRoomView(kMars35, kWest):
+ case MakeRoomView(kMars35, kEast):
+ case MakeRoomView(kMars60, kWest):
+ case MakeRoomView(kMars60, kEast):
+ GameState.setMarsInAirlock(true);
+ break;
+ default:
+ GameState.setMarsInAirlock(false);
+ break;
+ }
+}
+
+int16 Mars::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
+ int16 angle = Neighborhood::getStaticCompassAngle(room, dir);
+
+ switch (MakeRoomView(room, dir)) {
+ case MakeRoomView(kMars0A, kNorth):
+ angle -= 20;
+ break;
+ case MakeRoomView(kMars23, kNorth):
+ case MakeRoomView(kMars23, kSouth):
+ case MakeRoomView(kMars23, kEast):
+ case MakeRoomView(kMars23, kWest):
+ case MakeRoomView(kMars26, kNorth):
+ case MakeRoomView(kMars26, kSouth):
+ case MakeRoomView(kMars26, kEast):
+ case MakeRoomView(kMars26, kWest):
+ angle += 30;
+ break;
+ case MakeRoomView(kMars24, kNorth):
+ case MakeRoomView(kMars24, kSouth):
+ case MakeRoomView(kMars24, kEast):
+ case MakeRoomView(kMars24, kWest):
+ case MakeRoomView(kMars25, kNorth):
+ case MakeRoomView(kMars25, kSouth):
+ case MakeRoomView(kMars25, kEast):
+ case MakeRoomView(kMars25, kWest):
+ angle -= 30;
+ break;
+ case MakeRoomView(kMars54, kNorth):
+ case MakeRoomView(kMars54, kSouth):
+ case MakeRoomView(kMars54, kEast):
+ case MakeRoomView(kMars54, kWest):
+ angle += 90;
+ break;
+ case MakeRoomView(kMars56, kNorth):
+ case MakeRoomView(kMars56, kSouth):
+ case MakeRoomView(kMars56, kEast):
+ case MakeRoomView(kMars56, kWest):
+ angle += 180;
+ break;
+ case MakeRoomView(kMars58, kNorth):
+ case MakeRoomView(kMars58, kSouth):
+ case MakeRoomView(kMars58, kEast):
+ case MakeRoomView(kMars58, kWest):
+ angle -= 90;
+ break;
+ }
+
+ return angle;
+}
+
+void Mars::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) {
+ Neighborhood::getExitCompassMove(exitEntry, compassMove);
+
+ if (exitEntry.room == kMars43 && exitEntry.direction == kEast) {
+ compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kMarsFrameDuration, 90);
+ compassMove.insertFaderKnot(exitEntry.movieStart + 32 * kMarsFrameDuration, 270);
+ } else if (exitEntry.room == kMars46 && exitEntry.direction == kWest && exitEntry.altCode != kAltMarsPodAtMars45) {
+ compassMove.makeTwoKnotFaderSpec(kMarsMovieScale, exitEntry.movieStart, 270, exitEntry.movieEnd, 360);
+ compassMove.insertFaderKnot(exitEntry.movieStart + 43 * kMarsFrameDuration, 270);
+ compassMove.insertFaderKnot(exitEntry.movieStart + 58 * kMarsFrameDuration, 360);
+ }
+}
+
+void Mars::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) {
+ switch (entry.extra) {
+ case kMarsTakePodToMars45:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 0, entry.movieEnd, 180);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 3), 30);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 11), 10);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 14), 40);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 16), 30);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 23), 100);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 31), 70);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 34), 100);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 37), 85);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 42), 135);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 44), 125);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 46), 145);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 49), 160);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 51), 180);
+ break;
+ case kMars35WestSpinAirlockToEast:
+ case kMars60WestSpinAirlockToEast:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 270);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale, 90);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale * 3, 270);
+ break;
+ case kMars35EastSpinAirlockToWest:
+ case kMars60EastSpinAirlockToWest:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 270, entry.movieEnd, 90);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale, 270);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale * 3, 90);
+ break;
+ case kMars52SpinLeft:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, entry.movieEnd, kMars54Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars54Compass);
+ break;
+ case kMars52SpinRight:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, entry.movieEnd, kMars58Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars58Compass);
+ break;
+ case kMars52Extend:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass,
+ entry.movieEnd, kMars52Compass + kMarsShieldPanelOffsetAngle);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars52Compass + kMarsShieldPanelOffsetAngle);
+ break;
+ case kMars53Retract:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart,
+ kMars52Compass + kMarsShieldPanelOffsetAngle, entry.movieEnd, kMars52Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass + kMarsShieldPanelOffsetAngle);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars52Compass);
+ break;
+ case kMars56ExtendWithBomb:
+ case kMars56ExtendNoBomb:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass,
+ entry.movieEnd, kMars56Compass - kMarsShieldPanelOffsetAngle);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars56Compass - kMarsShieldPanelOffsetAngle);
+ break;
+ case kMars57RetractWithBomb:
+ case kMars57RetractNoBomb:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart,
+ kMars56Compass - kMarsShieldPanelOffsetAngle, entry.movieEnd, kMars56Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass - kMarsShieldPanelOffsetAngle);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars56Compass);
+ break;
+ case kMars54SpinLeft:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars54Compass, entry.movieEnd, kMars56Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars54Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars56Compass);
+ break;
+ case kMars54SpinRight:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars54Compass, entry.movieEnd, kMars52Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars54Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars52Compass);
+ break;
+ case kMars56SpinLeft:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass,
+ entry.movieEnd, kMars58Compass + 360);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars58Compass + 360);
+ break;
+ case kMars56SpinRight:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, entry.movieEnd, kMars54Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars54Compass);
+ break;
+ case kMars58SpinLeft:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars58Compass,
+ entry.movieEnd, kMars52Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars58Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars52Compass);
+ break;
+ case kMars58SpinRight:
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart,
+ kMars58Compass + 360, entry.movieEnd, kMars56Compass);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars58Compass + 360);
+ compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars56Compass);
+ break;
+ default:
+ Neighborhood::getExtraCompassMove(entry, compassMove);
+ }
+}
+
+void Mars::loadAmbientLoops() {
+ RoomID room = GameState.getCurrentRoom();
+
+ if ((room >= kMars0A && room <= kMars21) || (room >= kMars41 && room <= kMars43)) {
+ if (GameState.getMarsSeenTimeStream())
+ loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF");
+ } else if (room >= kMars22 && room <= kMars31South) {
+ loadLoopSound1("Sounds/Mars/Reception.02.22K.8.AIFF", 0x100 / 4);
+ } else if (room >= kMars32 && room <= kMars34) {
+ loadLoopSound1("Sounds/Mars/Pod Room Ambient.22K.8.AIFF");
+ } else if (room == kMars35) {
+ if (getAirQuality(room) == kAirQualityVacuum)
+ loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF");
+ else
+ loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF", 0x100 / 2);
+ } else if (room >= kMars36 && room <= kMars39) {
+ loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF");
+ } else if (room >= kMars45 && room <= kMars51) {
+ loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF");
+ } else if (room >= kMars52 && room <= kMars58) {
+ loadLoopSound1("Sounds/Mars/ReactorLoop.22K.8.AIFF");
+ } else if (room == kMars60) {
+ if (getAirQuality(room) == kAirQualityVacuum)
+ loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF");
+ else
+ loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF", 0x100 / 2);
+ } else if (room >= kMarsMaze004 && room <= kMarsMaze200) {
+ loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF");
+ } else if (room == kMarsRobotShuttle) {
+ loadLoopSound1("Sounds/Mars/Robot Shuttle.22K.8.AIFF");
+ }
+
+ if (!_noAirFuse.isFuseLit()) {
+ switch (room) {
+ case kMars02:
+ case kMars05:
+ case kMars06:
+ case kMars07:
+ case kMars08:
+ loadLoopSound2("Sounds/Mars/Gantry Loop.aiff", 0x100, 0, 0);
+ break;
+ // Robot at maze 48
+ case kMarsMaze037:
+ if (GameState.isCurrentDoorOpen())
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2);
+ else
+ loadLoopSound2("");
+ break;
+ case kMarsMaze038:
+ case kMarsMaze039:
+ case kMarsMaze049:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100);
+ break;
+ case kMarsMaze050:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4);
+ break;
+ case kMarsMaze051:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2);
+ break;
+ case kMarsMaze052:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4);
+ break;
+ case kMarsMaze042:
+ case kMarsMaze053:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 8);
+ break;
+ case kMarsMaze058:
+ if (GameState.isCurrentDoorOpen())
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4);
+ else
+ loadLoopSound2("");
+ break;
+ // Robot at 151
+ case kMarsMaze148:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100);
+ break;
+ case kMarsMaze147:
+ case kMarsMaze149:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4);
+ break;
+ case kMarsMaze146:
+ case kMarsMaze152:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2);
+ break;
+ case kMarsMaze145:
+ case kMarsMaze153:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4);
+ break;
+ // Robots at 80 and 82.
+ case kMarsMaze079:
+ case kMarsMaze081:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100);
+ break;
+ case kMarsMaze078:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4);
+ break;
+ case kMarsMaze083:
+ if (GameState.isCurrentDoorOpen())
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4);
+ else
+ loadLoopSound2("");
+ break;
+ case kMarsMaze118:
+ case kMarsMaze076:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2);
+ break;
+ case kMarsMaze074:
+ case kMarsMaze117:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4);
+ break;
+ // Robot at 94
+ case kMarsMaze093:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100);
+ break;
+ case kMarsMaze091:
+ case kMarsMaze092:
+ case kMarsMaze098:
+ case kMarsMaze101:
+ case kMarsMaze100:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4);
+ break;
+ case kMarsMaze090:
+ case kMarsMaze099:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2);
+ break;
+ case kMarsMaze089:
+ if (GameState.isCurrentDoorOpen())
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2);
+ break;
+ case kMarsMaze178:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4);
+ break;
+ // Robot at 197
+ case kMarsMaze191:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100);
+ break;
+ case kMarsMaze190:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4);
+ break;
+ case kMarsMaze198:
+ case kMarsMaze189:
+ loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2);
+ break;
+ default:
+ loadLoopSound2("");
+ break;
+ }
+ }
+}
+
+void Mars::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kMars02, kSouth):
+ case MakeRoomView(kMars19, kEast):
+ case MakeRoomView(kMars22, kNorth):
+ case MakeRoomView(kMars43, kEast):
+ case MakeRoomView(kMars51, kEast):
+ case MakeRoomView(kMars56, kEast):
+ case MakeRoomView(kMars60, kWest):
+ case MakeRoomView(kMarsMaze004, kWest):
+ case MakeRoomView(kMarsMaze009, kWest):
+ case MakeRoomView(kMarsMaze012, kWest):
+ case MakeRoomView(kMarsMaze037, kWest):
+ case MakeRoomView(kMarsMaze047, kNorth):
+ case MakeRoomView(kMarsMaze052, kWest):
+ case MakeRoomView(kMarsMaze057, kNorth):
+ case MakeRoomView(kMarsMaze071, kWest):
+ case MakeRoomView(kMarsMaze081, kNorth):
+ case MakeRoomView(kMarsMaze088, kWest):
+ case MakeRoomView(kMarsMaze093, kWest):
+ case MakeRoomView(kMarsMaze115, kNorth):
+ case MakeRoomView(kMarsMaze120, kWest):
+ case MakeRoomView(kMarsMaze126, kEast):
+ case MakeRoomView(kMarsMaze133, kNorth):
+ case MakeRoomView(kMarsMaze144, kNorth):
+ case MakeRoomView(kMarsMaze156, kEast):
+ case MakeRoomView(kMarsMaze162, kNorth):
+ case MakeRoomView(kMarsMaze177, kWest):
+ case MakeRoomView(kMarsMaze180, kNorth):
+ case MakeRoomView(kMarsMaze187, kWest):
+ case MakeRoomView(kMarsMaze199, kWest):
+ makeContinuePoint();
+ break;
+ case MakeRoomView(kMars05, kEast):
+ case MakeRoomView(kMars06, kEast):
+ case MakeRoomView(kMars07, kEast):
+ if (GameState.getMarsSecurityDown())
+ makeContinuePoint();
+ break;
+ case MakeRoomView(kMars46, kSouth):
+ if (!GameState.getMarsSeenRobotAtReactor())
+ makeContinuePoint();
+ break;
+ case MakeRoomView(kMars46, kWest):
+ if (GameState.getMarsAvoidedReactorRobot())
+ makeContinuePoint();
+ break;
+ }
+}
+
+void Mars::launchMaze007Robot() {
+ startExtraLongSequence(kMarsMaze007RobotApproach, kMarsMaze007RobotDeath, kExtraCompletedFlag, kFilterAllInput);
+ scheduleEvent(kMaze007RobotLoopingTime, kMarsMovieScale, kMaze007RobotLoopingEvent);
+}
+
+void Mars::launchMaze015Robot() {
+ startExtraLongSequence(kMarsMaze015SouthRobotApproach, kMarsMaze015SouthRobotDeath, kExtraCompletedFlag, kFilterAllInput);
+ scheduleEvent(kMaze015RobotLoopingTime, kMarsMovieScale, kMaze015RobotLoopingEvent);
+}
+
+void Mars::launchMaze101Robot() {
+ startExtraLongSequence(kMarsMaze101EastRobotApproach, kMarsMaze101EastRobotDeath, kExtraCompletedFlag, kFilterAllInput);
+ scheduleEvent(kMaze101RobotLoopingTime, kMarsMovieScale, kMaze101RobotLoopingEvent);
+}
+
+void Mars::launchMaze104Robot() {
+ startExtraLongSequence(kMarsMaze104WestLoop, kMarsMaze104WestDeath, kExtraCompletedFlag, kFilterAllInput);
+ scheduleEvent(kMaze104RobotLoopingTime, kMarsMovieScale, kMaze104RobotLoopingEvent);
+}
+
+void Mars::launchMaze133Robot() {
+ startExtraLongSequence(kMarsMaze133SouthApproach, kMarsMaze133SouthDeath, kExtraCompletedFlag, kFilterAllInput);
+ scheduleEvent(kMaze133RobotLoopingTime, kMarsMovieScale, kMaze133RobotLoopingEvent);
+}
+
+void Mars::launchMaze136Robot() {
+ startExtraLongSequence(kMarsMaze136NorthApproach, kMarsMaze136NorthDeath, kExtraCompletedFlag, kFilterAllInput);
+ scheduleEvent(kMaze136RobotLoopingTime, kMarsMovieScale, kMaze136RobotLoopingEvent);
+}
+
+void Mars::launchMaze184Robot() {
+ startExtraLongSequence(kMarsMaze184WestLoop, kMarsMaze184WestDeath, kExtraCompletedFlag, kFilterAllInput);
+ scheduleEvent(kMaze184RobotLoopingTime, kMarsMovieScale, kMaze184RobotLoopingEvent);
+}
+
+void Mars::timerExpired(const uint32 eventType) {
+ switch (eventType) {
+ case kMaze007RobotLoopingEvent:
+ case kMaze015RobotLoopingEvent:
+ case kMaze101RobotLoopingEvent:
+ case kMaze104RobotLoopingEvent:
+ case kMaze133RobotLoopingEvent:
+ case kMaze136RobotLoopingEvent:
+ case kMaze184RobotLoopingEvent:
+ _interruptionFilter = kFilterNoInput;
+ break;
+ }
+}
+
+void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kMars18, kNorth):
+ if (GameState.getMarsPodAtUpperPlatform())
+ setCurrentAlternate(kAltMarsPodAtMars34);
+ break;
+ case MakeRoomView(kMars27, kEast):
+ case MakeRoomView(kMars29, kEast):
+ if (GameState.isTakenItemID(kMarsCard))
+ setCurrentAlternate(kAltMarsTookCard);
+ else
+ setCurrentAlternate(kAltMarsNormal);
+ break;
+ case MakeRoomView(kMars35, kEast):
+ case MakeRoomView(kMars35, kWest):
+ if (GameState.getMarsAirlockOpen())
+ setCurrentAlternate(kAltMars35AirlockWest);
+ else
+ setCurrentAlternate(kAltMars35AirlockEast);
+ break;
+ case MakeRoomView(kMars60, kEast):
+ case MakeRoomView(kMars60, kWest):
+ if (GameState.getMarsAirlockOpen())
+ setCurrentAlternate(kAltMars60AirlockEast);
+ else
+ setCurrentAlternate(kAltMars60AirlockWest);
+ break;
+ case MakeRoomView(kMars45, kNorth):
+ case MakeRoomView(kMars45, kSouth):
+ case MakeRoomView(kMars45, kEast):
+ case MakeRoomView(kMars45, kWest):
+ GameState.setMarsPodAtUpperPlatform(false);
+ setCurrentAlternate(kAltMarsPodAtMars45);
+ break;
+ case MakeRoomView(kMars46, kNorth):
+ case MakeRoomView(kMars46, kSouth):
+ case MakeRoomView(kMars46, kEast):
+ case MakeRoomView(kMars46, kWest):
+ case MakeRoomView(kMars47, kNorth):
+ case MakeRoomView(kMars47, kSouth):
+ case MakeRoomView(kMars47, kEast):
+ case MakeRoomView(kMars47, kWest):
+ if (GameState.getMarsPodAtUpperPlatform())
+ setCurrentAlternate(kAltMarsNormal);
+ else
+ setCurrentAlternate(kAltMarsPodAtMars45);
+ break;
+ case MakeRoomView(kMars48, kNorth):
+ case MakeRoomView(kMars48, kSouth):
+ case MakeRoomView(kMars48, kEast):
+ case MakeRoomView(kMars48, kWest):
+ case MakeRoomView(kMars49, kNorth):
+ case MakeRoomView(kMars49, kEast):
+ case MakeRoomView(kMars49, kWest):
+ if (GameState.isTakenItemID(kAirMask))
+ setCurrentAlternate(kAltMarsTookMask);
+ else
+ setCurrentAlternate(kAltMarsNormal);
+ break;
+ case MakeRoomView(kMars49, kSouth):
+ if (GameState.getMarsMaskOnFiller())
+ setCurrentAlternate(kAltMarsMaskOnFiller);
+ else if (GameState.isTakenItemID(kAirMask))
+ setCurrentAlternate(kAltMarsTookMask);
+ else
+ setCurrentAlternate(kAltMarsNormal);
+ break;
+ }
+
+ Neighborhood::arriveAt(room, direction);
+ checkAirlockDoors();
+ setUpReactorEnergyDrain();
+
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kMars0A, kNorth):
+ if (!GameState.getMarsSeenTimeStream())
+ startExtraLongSequence(kMarsArrivalFromTSA, kMars0AWatchShuttleDepart, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kMars07, kSouth):
+ case MakeRoomView(kMars13, kNorth):
+ if (!GameState.getMarsHeardCheckInMessage()) {
+ playSpotSoundSync(kMarsCheckInRequiredIn, kMarsCheckInRequiredOut);
+ GameState.setMarsHeardCheckInMessage(true);
+ }
+ break;
+ case MakeRoomView(kMars44, kWest):
+ if (GameState.getMarsReadyForShuttleTransport())
+ startUpFromFinishedSpaceChase();
+ else if (GameState.getMarsFinishedCanyonChase())
+ startUpFromSpaceChase();
+ else
+ _neighborhoodNotification.setNotificationFlags(kTimeForCanyonChaseFlag, kTimeForCanyonChaseFlag);
+ break;
+ case MakeRoomView(kMars10, kNorth):
+ if (!GameState.getMarsRobotThrownPlayer())
+ startExtraSequence(kRobotThrowsPlayer, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kMars11, kSouth):
+ case MakeRoomView(kMars12, kSouth):
+ setCurrentActivation(kActivationReadyForKiosk);
+ break;
+ case MakeRoomView(kMars15, kWest):
+ if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) {
+ playSpotSoundSync(kMarsShuttle2DepartedIn, kMarsShuttle2DepartedOut);
+ restoreStriding(kMars17, kWest, kAltMarsNormal);
+ GameState.setMarsSecurityDown(true);
+ }
+ break;
+ case MakeRoomView(kMars17, kNorth):
+ case MakeRoomView(kMars17, kSouth):
+ case MakeRoomView(kMars17, kEast):
+ case MakeRoomView(kMars17, kWest):
+ if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown())
+ forceStridingStop(kMars17, kWest, kAltMarsNormal);
+
+ if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) {
+ startExtraSequence(kRobotOnWayToShuttle, kExtraCompletedFlag, kFilterNoInput);
+ restoreStriding(kMars19, kWest, kAltMarsNormal);
+ GameState.setMarsSawRobotLeave(true);
+ }
+ break;
+ case MakeRoomView(kMars19, kNorth):
+ case MakeRoomView(kMars19, kSouth):
+ case MakeRoomView(kMars19, kWest):
+ if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave())
+ forceStridingStop(kMars19, kWest, kAltMarsNormal);
+
+ if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown())
+ forceStridingStop(kMars17, kWest, kAltMarsNormal);
+ break;
+ case MakeRoomView(kMars19, kEast):
+ if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave())
+ forceStridingStop(kMars19, kWest, kAltMarsNormal);
+
+ if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown())
+ forceStridingStop(kMars17, kWest, kAltMarsNormal);
+ break;
+ case MakeRoomView(kMars32, kNorth):
+ if (!GameState.getMarsPodAtUpperPlatform()) {
+ playSpotSoundSync(kMarsPodArrivedUpperPlatformIn, kMarsPodArrivedUpperPlatformOut);
+ GameState.setMarsPodAtUpperPlatform(true);
+ }
+ break;
+ case MakeRoomView(kMars33North, kNorth):
+ setCurrentActivation(kActivationTunnelMapReady);
+ // Fall through...
+ case MakeRoomView(kMars33, kSouth):
+ case MakeRoomView(kMars33, kEast):
+ case MakeRoomView(kMars33, kWest):
+ case MakeRoomView(kMars32, kSouth):
+ case MakeRoomView(kMars32, kEast):
+ case MakeRoomView(kMars32, kWest):
+ if (!GameState.getMarsPodAtUpperPlatform())
+ GameState.setMarsPodAtUpperPlatform(true);
+ break;
+ case MakeRoomView(kMars34, kNorth):
+ startExtraSequence(kMars34NorthPodGreeting, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kMars34, kSouth):
+ case MakeRoomView(kMars45, kNorth):
+ setCurrentActivation(kActivateMarsPodClosed);
+ break;
+ case MakeRoomView(kMars35, kWest):
+ if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown())
+ forceStridingStop(kMars19, kWest, kAltMarsNormal);
+ // Fall through...
+ case MakeRoomView(kMars60, kEast):
+ if (!GameState.getMarsAirlockOpen())
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+ break;
+ case MakeRoomView(kMars35, kEast):
+ case MakeRoomView(kMars60, kWest):
+ if (GameState.getMarsAirlockOpen())
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+ break;
+ case MakeRoomView(kMars39, kWest):
+ if (GameState.getLastRoom() == kMarsMaze200)
+ GameState.setMarsPodAtUpperPlatform(false);
+ break;
+ case MakeRoomView(kMars45, kSouth):
+ // Set up maze doors here.
+ // Doing it here makes sure that it will be the same if the player comes
+ // back out of the maze and goes back in, but will vary if
+ // the player comes back down to the maze a second time.
+ GameState.setMarsMazeDoorPair1(_vm->getRandomBit());
+ GameState.setMarsMazeDoorPair2(_vm->getRandomBit());
+ GameState.setMarsMazeDoorPair3(_vm->getRandomBit());
+ GameState.setMarsArrivedBelow(true);
+ break;
+ case MakeRoomView(kMars48, kEast):
+ if (!GameState.getMarsSeenRobotAtReactor()) {
+ // Preload the looping sound...
+ loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0);
+ startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput);
+ } else if (!GameState.getMarsAvoidedReactorRobot()) {
+ loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0);
+ loopExtraSequence(kMars48RobotLoops);
+ _utilityFuse.primeFuse(kMarsRobotPatienceLimit);
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting));
+ _utilityFuse.lightFuse();
+ }
+ break;
+ case MakeRoomView(kMars48, kSouth):
+ if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) {
+ loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0);
+ _utilityFuse.primeFuse(kMarsRobotPatienceLimit);
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting));
+ _utilityFuse.lightFuse();
+ }
+ break;
+ case MakeRoomView(kMars49, kSouth):
+ if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) {
+ playSpotSoundSync(kMarsRobotTakesTransportIn, kMarsRobotTakesTransportOut);
+ playSpotSoundSync(kMarsPodDepartedLowerPlatformIn, kMarsPodDepartedLowerPlatformOut);
+ GameState.setMarsAvoidedReactorRobot(true);
+ GameState.setMarsPodAtUpperPlatform(true);
+ GameState.setScoringAvoidedRobot();
+ }
+
+ if (GameState.isTakenItemID(kAirMask))
+ setCurrentActivation(kActivateHotSpotAlways);
+ else if (GameState.getMarsMaskOnFiller())
+ setCurrentActivation(kActivateMaskOnFiller);
+ else
+ setCurrentActivation(kActivateMaskOnHolder);
+ break;
+ case MakeRoomView(kMars51, kWest):
+ case MakeRoomView(kMars50, kWest):
+ case MakeRoomView(kMars48, kWest):
+ if (GameState.getShieldOn())
+ g_shield->setItemState(kShieldNormal);
+ g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal);
+ _vm->resetEnergyDeathReason();
+ break;
+ case MakeRoomView(kMars52, kNorth):
+ case MakeRoomView(kMars52, kSouth):
+ case MakeRoomView(kMars52, kEast):
+ case MakeRoomView(kMars52, kWest):
+ case MakeRoomView(kMars54, kNorth):
+ case MakeRoomView(kMars54, kSouth):
+ case MakeRoomView(kMars54, kEast):
+ case MakeRoomView(kMars54, kWest):
+ case MakeRoomView(kMars56, kNorth):
+ case MakeRoomView(kMars56, kSouth):
+ case MakeRoomView(kMars56, kWest):
+ case MakeRoomView(kMars58, kNorth):
+ case MakeRoomView(kMars58, kSouth):
+ case MakeRoomView(kMars58, kEast):
+ case MakeRoomView(kMars58, kWest):
+ setCurrentActivation(kActivateReactorPlatformOut);
+ break;
+ case MakeRoomView(kMars56, kEast):
+ if (GameState.getMarsLockBroken()) {
+ setCurrentActivation(kActivateReactorAskOperation);
+ _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true);
+ } else if (GameState.getMarsLockFrozen()) {
+ setCurrentActivation(kActivateReactorReadyForCrowBar);
+ _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true);
+ _utilityFuse.primeFuse(kLockFreezeTimeLmit);
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::lockThawed));
+ _utilityFuse.lightFuse();
+ } else {
+ setCurrentActivation(kActivateReactorPlatformOut);
+ }
+ break;
+ case MakeRoomView(kMarsRobotShuttle, kEast):
+ setCurrentActivation(kActivationRobotHeadClosed);
+ break;
+ case MakeRoomView(kMarsMaze007, kNorth):
+ launchMaze007Robot();
+ break;
+ case MakeRoomView(kMarsMaze015, kSouth):
+ launchMaze015Robot();
+ break;
+ case MakeRoomView(kMarsMaze101, kEast):
+ launchMaze101Robot();
+ break;
+ case MakeRoomView(kMarsMaze104, kWest):
+ launchMaze104Robot();
+ break;
+ case MakeRoomView(kMarsMaze133, kSouth):
+ launchMaze133Robot();
+ break;
+ case MakeRoomView(kMarsMaze136, kNorth):
+ launchMaze136Robot();
+ break;
+ case MakeRoomView(kMarsMaze184, kWest):
+ launchMaze184Robot();
+ break;
+ case MakeRoomView(kMarsMaze199, kSouth):
+ GameState.setScoringThreadedMaze();
+ GameState.setMarsThreadedMaze(true);
+ break;
+ case MakeRoomView(kMarsDeathRoom, kNorth):
+ case MakeRoomView(kMarsDeathRoom, kSouth):
+ case MakeRoomView(kMarsDeathRoom, kEast):
+ case MakeRoomView(kMarsDeathRoom, kWest):
+ switch (GameState.getLastRoom()) {
+ case kMars39:
+ die(kDeathDidntLeaveBucket);
+ break;
+ case kMars46:
+ die(kDeathRunOverByPod);
+ break;
+ }
+ break;
+ }
+
+ checkAirMask();
+}
+
+void Mars::shieldOn() {
+ setUpReactorEnergyDrain();
+}
+
+void Mars::shieldOff() {
+ setUpReactorEnergyDrain();
+}
+
+void Mars::turnTo(const DirectionConstant direction) {
+ switch (MakeRoomView(GameState.getCurrentRoom(), direction)) {
+ case MakeRoomView(kMars27, kNorth):
+ case MakeRoomView(kMars27, kSouth):
+ case MakeRoomView(kMars27, kEast):
+ case MakeRoomView(kMars29, kNorth):
+ case MakeRoomView(kMars29, kSouth):
+ case MakeRoomView(kMars29, kEast):
+ if (GameState.isTakenItemID(kMarsCard))
+ setCurrentAlternate(kAltMarsTookCard);
+ break;
+ case MakeRoomView(kMars35, kNorth):
+ case MakeRoomView(kMars35, kSouth):
+ case MakeRoomView(kMars60, kNorth):
+ case MakeRoomView(kMars60, kSouth):
+ if (getCurrentActivation() == kActivateAirlockPressurized)
+ playSpotSoundSync(kMarsAirlockPressurizeIn, kMarsAirlockPressurizeOut);
+ break;
+ }
+
+ Neighborhood::turnTo(direction);
+
+ switch (MakeRoomView(GameState.getCurrentRoom(), direction)) {
+ case MakeRoomView(kMars11, kSouth):
+ case MakeRoomView(kMars12, kSouth):
+ setCurrentActivation(kActivationReadyForKiosk);
+ break;
+ case MakeRoomView(kMars18, kNorth):
+ if (GameState.getMarsPodAtUpperPlatform())
+ setCurrentAlternate(kAltMarsPodAtMars34);
+ break;
+ case MakeRoomView(kMars22, kSouth):
+ if (!GameState.getMarsHeardCheckInMessage()) {
+ playSpotSoundSync(kMarsCheckInRequiredIn, kMarsCheckInRequiredOut);
+ GameState.setMarsHeardCheckInMessage(true);
+ }
+ break;
+ case MakeRoomView(kMars34, kSouth):
+ case MakeRoomView(kMars45, kNorth):
+ setCurrentActivation(kActivateMarsPodClosed);
+ break;
+ case MakeRoomView(kMars34, kNorth):
+ startExtraSequence(kMars34NorthPodGreeting, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kMars35, kEast):
+ case MakeRoomView(kMars60, kWest):
+ if (GameState.getMarsAirlockOpen())
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+ break;
+ case MakeRoomView(kMars60, kEast):
+ if (!GameState.getMarsAirlockOpen())
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+ break;
+ case MakeRoomView(kMars35, kWest):
+ if (!GameState.getMarsAirlockOpen())
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+
+ // Do this here because this will be called after spinning the airlock after
+ // going through the gear room.
+ if (GameState.getMarsThreadedMaze())
+ GameState.setScoringThreadedGearRoom();
+ break;
+ case MakeRoomView(kMars48, kNorth):
+ if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot())
+ die(kDeathDidntGetOutOfWay);
+ break;
+ case MakeRoomView(kMars48, kEast):
+ if (!GameState.getMarsSeenRobotAtReactor()) {
+ // Preload the looping sound...
+ loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0);
+ startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput);
+ } else if (!GameState.getMarsAvoidedReactorRobot()) {
+ loopExtraSequence(kMars48RobotLoops);
+ } else if (GameState.isTakenItemID(kAirMask)) {
+ setCurrentAlternate(kAltMarsTookMask);
+ } else {
+ setCurrentAlternate(kAltMarsNormal);
+ }
+ break;
+ case MakeRoomView(kMars48, kWest):
+ if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot())
+ die(kDeathDidntGetOutOfWay);
+ else if (GameState.isTakenItemID(kAirMask))
+ setCurrentAlternate(kAltMarsTookMask);
+ else
+ setCurrentAlternate(kAltMarsNormal);
+ break;
+ case MakeRoomView(kMars49, kSouth):
+ if (GameState.isTakenItemID(kAirMask))
+ setCurrentActivation(kActivateHotSpotAlways);
+ else
+ setCurrentActivation(kActivateMaskOnHolder);
+ break;
+ case MakeRoomView(kMars52, kNorth):
+ case MakeRoomView(kMars52, kSouth):
+ case MakeRoomView(kMars52, kEast):
+ case MakeRoomView(kMars52, kWest):
+ case MakeRoomView(kMars54, kNorth):
+ case MakeRoomView(kMars54, kSouth):
+ case MakeRoomView(kMars54, kEast):
+ case MakeRoomView(kMars54, kWest):
+ case MakeRoomView(kMars56, kNorth):
+ case MakeRoomView(kMars56, kSouth):
+ case MakeRoomView(kMars56, kEast):
+ case MakeRoomView(kMars56, kWest):
+ case MakeRoomView(kMars58, kNorth):
+ case MakeRoomView(kMars58, kSouth):
+ case MakeRoomView(kMars58, kEast):
+ case MakeRoomView(kMars58, kWest):
+ setCurrentActivation(kActivateReactorPlatformOut);
+ break;
+ case MakeRoomView(kMarsMaze007, kNorth):
+ launchMaze007Robot();
+ break;
+ case MakeRoomView(kMarsMaze015, kSouth):
+ launchMaze015Robot();
+ break;
+ case MakeRoomView(kMarsMaze101, kEast):
+ launchMaze101Robot();
+ break;
+ case MakeRoomView(kMarsMaze104, kWest):
+ launchMaze104Robot();
+ break;
+ case MakeRoomView(kMarsMaze133, kSouth):
+ launchMaze133Robot();
+ break;
+ case MakeRoomView(kMarsMaze136, kNorth):
+ launchMaze136Robot();
+ break;
+ case MakeRoomView(kMarsMaze184, kWest):
+ launchMaze184Robot();
+ break;
+ }
+}
+
+void Mars::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) {
+ switch (hotspot->getObjectID()) {
+ case kMars57RedMoveSpotID:
+ case kMars57YellowMoveSpotID:
+ case kMars57GreenMoveSpotID:
+ if (!_choiceHighlight.choiceHighlighted(hotspot->getObjectID() - kMars57RedMoveSpotID))
+ hotspot->setActive();
+ break;
+ case kMars57BlueMoveSpotID:
+ if (_reactorStage >= 2 && !_choiceHighlight.choiceHighlighted(3))
+ hotspot->setActive();
+ break;
+ case kMars57PurpleMoveSpotID:
+ if (_reactorStage == 3 && !_choiceHighlight.choiceHighlighted(4))
+ hotspot->setActive();
+ break;
+ default:
+ Neighborhood::activateOneHotspot(entry, hotspot);
+ break;
+ }
+}
+
+void Mars::activateHotspots() {
+ InventoryItem *item;
+
+ Neighborhood::activateHotspots();
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars48, kEast):
+ if ((_navMovie.getFlags() & kLoopTimeBase) != 0 && _vm->getDragType() == kDragInventoryUse)
+ _vm->getAllHotspots().activateOneHotspot(kAttackRobotHotSpotID);
+ break;
+ case MakeRoomView(kMars56, kEast):
+ switch (getCurrentActivation()) {
+ case kActivateReactorReadyForNitrogen:
+ item = (InventoryItem *)_vm->getAllItems().findItemByID(kNitrogenCanister);
+ if (item->getItemState() != kNitrogenFull)
+ _vm->getAllHotspots().deactivateOneHotspot(kMars57DropNitrogenSpotID);
+ // Fall through...
+ case kActivateReactorReadyForCrowBar:
+ _vm->getAllHotspots().activateOneHotspot(kMars57CantOpenPanelSpotID);
+ break;
+ }
+ break;
+ case MakeRoomView(kMarsRobotShuttle, kEast):
+ if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag))
+ _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleMapChipSpotID);
+ else
+ _vm->getAllHotspots().activateOneHotspot(kRobotShuttleMapChipSpotID);
+
+ if (_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag))
+ _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleOpticalChipSpotID);
+ else
+ _vm->getAllHotspots().activateOneHotspot(kRobotShuttleOpticalChipSpotID);
+
+ if (_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag))
+ _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleShieldChipSpotID);
+ else
+ _vm->getAllHotspots().activateOneHotspot(kRobotShuttleShieldChipSpotID);
+ break;
+ default:
+ if (_privateFlags.getFlag(kMarsPrivateInSpaceChaseFlag)) {
+ if (GameState.getMarsReadyForShuttleTransport()) {
+ _shuttleTransportSpot.setActive();
+ } else {
+ _energyChoiceSpot.setActive();
+ _gravitonChoiceSpot.setActive();
+ _tractorChoiceSpot.setActive();
+ if (_weaponSelection != kNoWeapon)
+ _shuttleViewSpot.setActive();
+ }
+ }
+ break;
+ }
+}
+
+void Mars::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+ switch (clickedSpot->getObjectID()) {
+ case kMars11NorthKioskSpotID:
+ case kMars12NorthKioskSpotID:
+ playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut);
+ Neighborhood::clickInHotspot(input, clickedSpot);
+ break;
+ case kMars11NorthKioskSightsSpotID:
+ case kMars12NorthKioskSightsSpotID:
+ playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut);
+ if (!startExtraSequenceSync(kMarsSightsInfo, kFilterAllInput))
+ showExtraView(kMarsInfoKioskIntro);
+ break;
+ case kMars11NorthKioskColonySpotID:
+ case kMars12NorthKioskColonySpotID:
+ playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut);
+ if (!startExtraSequenceSync(kMarsColonyInfo, kFilterAllInput))
+ showExtraView(kMarsInfoKioskIntro);
+ break;
+ case kMars33NorthMonitorSpotID:
+ switch (_lastExtra) {
+ case kMars33SlideShow1:
+ startExtraSequence(kMars33SlideShow2, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars33SlideShow2:
+ startExtraSequence(kMars33SlideShow3, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars33SlideShow3:
+ startExtraSequence(kMars33SlideShow4, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars33SlideShow4:
+ // Should never happen...
+ default:
+ startExtraSequence(kMars33SlideShow1, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ break;
+ case kMars34SouthOpenStorageSpotID:
+ if (GameState.isTakenItemID(kCrowbar))
+ startExtraSequence(kMars34SpotOpenNoBar, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMars34SpotOpenWithBar, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars34SouthCloseStorageSpotID:
+ if (GameState.isTakenItemID(kCrowbar))
+ startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars35WestPressurizeSpotID:
+ case kMars35EastPressurizeSpotID:
+ case kMars60WestPressurizeSpotID:
+ case kMars60EastPressurizeSpotID:
+ playSpotSoundSync(kMarsAirlockButtonBeepIn, kMarsAirlockButtonBeepOut);
+ playSpotSoundSync(kMarsAirlockPressurizeIn, kMarsAirlockPressurizeOut);
+ setCurrentActivation(kActivateAirlockPressurized);
+ break;
+ case kMars45NorthOpenStorageSpotID:
+ if (GameState.isTakenItemID(kCrowbar))
+ startExtraSequence(kMars45SpotOpenNoBar, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMars45SpotOpenWithBar, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars45NorthCloseStorageSpotID:
+ if (GameState.isTakenItemID(kCrowbar))
+ startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars56ExtractSpotID:
+ if (GameState.isTakenItemID(kCardBomb)) {
+ startExtraSequence(kMars56ExtendNoBomb, kExtraCompletedFlag, kFilterNoInput);
+ setCurrentActivation(kActivateReactorPlatformIn);
+ } else {
+ startExtraSequence(kMars56ExtendWithBomb, kExtraCompletedFlag, kFilterNoInput);
+ setCurrentActivation(kActivateReactorAskLowerScreen);
+ }
+ break;
+ case kMars57UndoMoveSpotID:
+ playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut);
+ doUndoOneGuess();
+ break;
+ case kMars57RedMoveSpotID:
+ playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut);
+ doReactorGuess(0);
+ break;
+ case kMars57YellowMoveSpotID:
+ playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut);
+ doReactorGuess(1);
+ break;
+ case kMars57GreenMoveSpotID:
+ playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut);
+ doReactorGuess(2);
+ break;
+ case kMars57BlueMoveSpotID:
+ playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut);
+ doReactorGuess(3);
+ break;
+ case kMars57PurpleMoveSpotID:
+ playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut);
+ doReactorGuess(4);
+ break;
+ case kShuttleEnergySpotID:
+ case kShuttleGravitonSpotID:
+ case kShuttleTractorSpotID:
+ case kShuttleViewSpotID:
+ case kShuttleTransportSpotID:
+ spaceChaseClick(input, clickedSpot->getObjectID());
+ break;
+ default:
+ Neighborhood::clickInHotspot(input, clickedSpot);
+ break;
+ }
+}
+
+InputBits Mars::getInputFilter() {
+ InputBits result = Neighborhood::getInputFilter();
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars49, kSouth):
+ if (GameState.getMarsMaskOnFiller())
+ // Can't move when mask is on filler.
+ result &= ~kFilterAllDirections;
+ break;
+ case MakeRoomView(kMars52, kNorth):
+ case MakeRoomView(kMars52, kSouth):
+ case MakeRoomView(kMars52, kEast):
+ case MakeRoomView(kMars52, kWest):
+ case MakeRoomView(kMars54, kNorth):
+ case MakeRoomView(kMars54, kSouth):
+ case MakeRoomView(kMars54, kEast):
+ case MakeRoomView(kMars54, kWest):
+ case MakeRoomView(kMars56, kNorth):
+ case MakeRoomView(kMars56, kSouth):
+ case MakeRoomView(kMars56, kEast):
+ case MakeRoomView(kMars56, kWest):
+ case MakeRoomView(kMars58, kNorth):
+ case MakeRoomView(kMars58, kSouth):
+ case MakeRoomView(kMars58, kEast):
+ case MakeRoomView(kMars58, kWest):
+ if (_privateFlags.getFlag(kMarsPrivatePlatformZoomedInFlag))
+ // Can't move when platform is extended.
+ result &= ~kFilterAllDirections;
+ break;
+ case MakeRoomView(kMars44, kWest):
+ if (_canyonChaseMovie.isMovieValid() && _canyonChaseMovie.isRunning())
+ result &= ~kFilterAllDirections;
+ break;
+ }
+
+ return result;
+}
+
+// Only called when trying to pick up an item and the player can't (because
+// the inventory is too full or because the player lets go of the item before
+// dropping it into the inventory).
+Hotspot *Mars::getItemScreenSpot(Item *item, DisplayElement *element) {
+ HotSpotID destSpotID;
+
+ switch (item->getObjectID()) {
+ case kCardBomb:
+ destSpotID = kMars57GrabBombSpotID;
+ break;
+ case kMarsCard:
+ destSpotID = kMars31SouthCardSpotID;
+ break;
+ case kAirMask:
+ if (GameState.getMarsMaskOnFiller())
+ destSpotID = kMars49AirFillingDropSpotID;
+ else
+ destSpotID = kMars49AirMaskSpotID;
+ break;
+ case kCrowbar:
+ if (GameState.getCurrentRoom() == kMars34)
+ destSpotID = kMars34SouthCrowbarSpotID;
+ else
+ destSpotID = kMars45NorthCrowbarSpotID;
+ break;
+ case kMapBiochip:
+ destSpotID = kRobotShuttleMapChipSpotID;
+ break;
+ case kOpticalBiochip:
+ destSpotID = kRobotShuttleOpticalChipSpotID;
+ break;
+ case kShieldBiochip:
+ destSpotID = kRobotShuttleShieldChipSpotID;
+ break;
+ default:
+ destSpotID = kNoHotSpotID;
+ break;
+ }
+
+ if (destSpotID == kNoHotSpotID)
+ return Neighborhood::getItemScreenSpot(item, element);
+
+ return _vm->getAllHotspots().findHotspotByID(destSpotID);
+}
+
+void Mars::takeItemFromRoom(Item *item) {
+ switch (item->getObjectID()) {
+ case kAirMask:
+ setCurrentAlternate(kAltMarsTookMask);
+ break;
+ case kCardBomb:
+ _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, true);
+ break;
+ case kMapBiochip:
+ _privateFlags.setFlag(kMarsPrivateGotMapChipFlag, true);
+ break;
+ case kShieldBiochip:
+ _privateFlags.setFlag(kMarsPrivateGotShieldChipFlag, true);
+ break;
+ case kOpticalBiochip:
+ _privateFlags.setFlag(kMarsPrivateGotOpticalChipFlag, true);
+ break;
+ }
+
+ Neighborhood::takeItemFromRoom(item);
+}
+
+void Mars::pickedUpItem(Item *item) {
+ switch (item->getObjectID()) {
+ case kAirMask:
+ setCurrentActivation(kActivateHotSpotAlways);
+ if (!GameState.getScoringGotOxygenMask()) {
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM48SB", false, kWarningInterruption);
+ GameState.setScoringGotOxygenMask();
+ }
+ break;
+ case kCrowbar:
+ GameState.setScoringGotCrowBar();
+ g_AIArea->checkMiddleArea();
+ break;
+ case kMarsCard:
+ GameState.setScoringGotMarsCard();
+ g_AIArea->checkMiddleArea();
+ break;
+ case kCardBomb:
+ GameState.setScoringGotCardBomb();
+ if (GameState.getMarsLockBroken()) {
+ startExtraSequence(kMars57BackToNormal, kExtraCompletedFlag, kFilterNoInput);
+ GameState.setMarsLockBroken(false);
+ }
+
+ _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, false);
+ break;
+ case kMapBiochip:
+ if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) &&
+ _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) {
+ GameState.setMarsFinished(true);
+ GameState.setScoringMarsGandhi();
+ startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kShieldBiochip:
+ if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) &&
+ _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) {
+ GameState.setMarsFinished(true);
+ GameState.setScoringMarsGandhi();
+ startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kOpticalBiochip:
+ g_opticalChip->addAries();
+ GameState.setScoringGotMarsOpMemChip();
+
+ if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) &&
+ _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) {
+ GameState.setMarsFinished(true);
+ GameState.setScoringMarsGandhi();
+ startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ }
+}
+
+void Mars::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
+ if (dropSpot->getObjectID() == kAttackRobotHotSpotID) {
+ _attackingItem = (InventoryItem *)item;
+ startExtraSequence(kMars48RobotDefends, kExtraCompletedFlag, kFilterNoInput);
+ loadLoopSound2("");
+ } else {
+ switch (item->getObjectID()) {
+ case kMarsCard:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ if (dropSpot && dropSpot->getObjectID() == kMars34NorthCardDropSpotID)
+ startExtraSequence(kMarsTurnOnPod, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kNitrogenCanister:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ if (dropSpot && dropSpot->getObjectID() == kMars57DropNitrogenSpotID)
+ startExtraSequence(kMars57FreezeLock, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kCrowbar:
+ _utilityFuse.stopFuse();
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ if (dropSpot && dropSpot->getObjectID() == kMars57DropCrowBarSpotID)
+ startExtraSequence(kMars57BreakLock, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kAirMask:
+ if (dropSpot) {
+ if (dropSpot->getObjectID() == kMars49AirFillingDropSpotID) {
+ if (!GameState.getMarsMaskOnFiller()) {
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ startExtraSequence(kMars49SouthViewMaskFilling, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ setCurrentActivation(kActivateMaskOnFiller);
+ setCurrentAlternate(kAltMarsMaskOnFiller);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ }
+ } else if (dropSpot->getObjectID() == kMars49AirMaskSpotID) {
+ setCurrentAlternate(kAltMarsNormal);
+ setCurrentActivation(kActivateMaskOnHolder);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ }
+ }
+ break;
+ case kCardBomb:
+ _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, false);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ case kMapBiochip:
+ _privateFlags.setFlag(kMarsPrivateGotMapChipFlag, false);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ case kShieldBiochip:
+ _privateFlags.setFlag(kMarsPrivateGotShieldChipFlag, false);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ case kOpticalBiochip:
+ _privateFlags.setFlag(kMarsPrivateGotOpticalChipFlag, false);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ default:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ }
+ }
+}
+
+void Mars::robotTiredOfWaiting() {
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kMars48, kEast)) {
+ if (_attackingItem) {
+ startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput);
+ loadLoopSound2("");
+ } else {
+ _privateFlags.setFlag(kMarsPrivateRobotTiredOfWaitingFlag, true);
+ }
+ } else {
+ die(kDeathDidntGetOutOfWay);
+ }
+}
+
+void Mars::turnLeft() {
+ if (isEventTimerRunning())
+ cancelEvent();
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars34, kSouth):
+ if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) {
+ _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, true);
+ if (GameState.isTakenItemID(kCrowbar))
+ startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ Neighborhood::turnLeft();
+ }
+ break;
+ case MakeRoomView(kMars45, kNorth):
+ if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) {
+ _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, true);
+ if (GameState.isTakenItemID(kCrowbar))
+ startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ Neighborhood::turnLeft();
+ }
+ break;
+ default:
+ Neighborhood::turnLeft();
+ break;
+ }
+}
+
+void Mars::turnRight() {
+ if (isEventTimerRunning())
+ cancelEvent();
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars34, kSouth):
+ if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) {
+ _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, true);
+ if (GameState.isTakenItemID(kCrowbar))
+ startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ Neighborhood::turnRight();
+ }
+ break;
+ case MakeRoomView(kMars45, kNorth):
+ if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) {
+ _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, true);
+ if (GameState.isTakenItemID(kCrowbar))
+ startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ Neighborhood::turnRight();
+ }
+ break;
+ default:
+ Neighborhood::turnRight();
+ break;
+ }
+}
+
+void Mars::receiveNotification(Notification *notification, const NotificationFlags flag) {
+ InventoryItem *item;
+
+ Neighborhood::receiveNotification(notification, flag);
+
+ if ((flag & kExtraCompletedFlag) != 0) {
+ _interruptionFilter = kFilterAllInput;
+
+ switch (_lastExtra) {
+ case kMarsArrivalFromTSA:
+ GameState.setMarsSeenTimeStream(true);
+ loadAmbientLoops();
+ playSpotSoundSync(kMarsShuttle1DepartedIn, kMarsShuttle1DepartedOut);
+ makeContinuePoint();
+ break;
+ case kRobotThrowsPlayer:
+ GameState.setMarsRobotThrownPlayer(true);
+ GameState.setScoringThrownByRobot();
+ restoreStriding(kMars08, kNorth, kAltMarsNormal);
+ arriveAt(kMars08, kNorth);
+ if (!GameState.getMarsHeardUpperPodMessage()) {
+ playSpotSoundSync(kMarsPodDepartedUpperPlatformIn,
+ kMarsPodDepartedUpperPlatformOut);
+ GameState.setMarsHeardUpperPodMessage(true);
+ }
+ break;
+ case kMarsInfoKioskIntro:
+ GameState.setScoringSawMarsKiosk();
+ setCurrentActivation(kActivationKioskChoice);
+ break;
+ case kMars33SlideShow4:
+ GameState.setScoringSawTransportMap();
+ setCurrentActivation(kActivateHotSpotAlways);
+ break;
+ case kMars34SpotOpenNoBar:
+ case kMars34SpotOpenWithBar:
+ case kMars45SpotOpenNoBar:
+ case kMars45SpotOpenWithBar:
+ _privateFlags.setFlag(kMarsPrivatePodStorageOpenFlag, true);
+ setCurrentActivation(kActivateMarsPodOpen);
+ break;
+ case kMars34SpotCloseNoBar:
+ case kMars34SpotCloseWithBar:
+ case kMars45SpotCloseNoBar:
+ case kMars45SpotCloseWithBar:
+ _privateFlags.setFlag(kMarsPrivatePodStorageOpenFlag, false);
+ setCurrentActivation(kActivateMarsPodClosed);
+ if (_privateFlags.getFlag(kMarsPrivatePodTurnLeftFlag)) {
+ _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, false);
+ turnLeft();
+ } else if (_privateFlags.getFlag(kMarsPrivatePodTurnRightFlag)) {
+ _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, false);
+ turnRight();
+ }
+ break;
+ case kMarsTurnOnPod:
+ item = (InventoryItem *)_vm->getAllItems().findItemByID(kMarsCard);
+ _vm->addItemToInventory(item);
+ GameState.setScoringTurnedOnTransport();
+ loadLoopSound1("");
+ loadLoopSound2("");
+ startExtraSequence(kMarsTakePodToMars45, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMarsTakePodToMars45:
+ arriveAt(kMars45, kSouth);
+ break;
+ case kMars35WestSpinAirlockToEast:
+ GameState.setMarsAirlockOpen(false);
+ setCurrentAlternate(kAltMars35AirlockEast);
+ turnTo(kWest);
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+ g_airMask->airQualityChanged();
+ checkAirMask();
+ loadAmbientLoops();
+ break;
+ case kMars35EastSpinAirlockToWest:
+ GameState.setMarsAirlockOpen(true);
+ setCurrentAlternate(kAltMars35AirlockWest);
+ turnTo(kEast);
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+ g_airMask->airQualityChanged();
+ checkAirMask();
+ loadAmbientLoops();
+ break;
+ case kMars48RobotApproaches:
+ loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0);
+ GameState.setMarsSeenRobotAtReactor(true);
+ loopExtraSequence(kMars48RobotLoops);
+ _utilityFuse.primeFuse(kMarsRobotPatienceLimit);
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting));
+ _utilityFuse.lightFuse();
+ break;
+ case kMars48RobotDefends:
+ _vm->addItemToInventory(_attackingItem);
+ _attackingItem = 0;
+ if (_privateFlags.getFlag(kMarsPrivateRobotTiredOfWaitingFlag)) {
+ startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput);
+ loadLoopSound2("", 0x100, 0, 0);
+ } else {
+ loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0);
+ loopExtraSequence(kMars48RobotLoops, kExtraCompletedFlag);
+ }
+ break;
+ case kMars48RobotKillsPlayer:
+ loadLoopSound2("");
+ die(kDeathDidntGetOutOfWay);
+ break;
+ case kMars49SouthViewMaskFilling:
+ setCurrentActivation(kActivateMaskOnFiller);
+ setCurrentAlternate(kAltMarsMaskOnFiller);
+ GameState.setMarsMaskOnFiller(true);
+ break;
+ case kMars58SpinLeft:
+ case kMars54SpinRight:
+ GameState.setScoringActivatedPlatform();
+ arriveAt(kMars52, kEast);
+ break;
+ case kMars52SpinLeft:
+ case kMars56SpinRight:
+ GameState.setScoringActivatedPlatform();
+ arriveAt(kMars54, kEast);
+ break;
+ case kMars54SpinLeft:
+ case kMars58SpinRight:
+ GameState.setScoringActivatedPlatform();
+ arriveAt(kMars56, kEast);
+ break;
+ case kMars56SpinLeft:
+ case kMars52SpinRight:
+ GameState.setScoringActivatedPlatform();
+ arriveAt(kMars58, kEast);
+ break;
+ case kMars52Extend:
+ case kMars54Extend:
+ case kMars56ExtendNoBomb:
+ case kMars58Extend:
+ GameState.setScoringActivatedPlatform();
+ setCurrentActivation(kActivateReactorPlatformIn);
+ _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true);
+ break;
+ case kMars53Retract:
+ case kMars55Retract:
+ case kMars57RetractWithBomb:
+ case kMars57RetractNoBomb:
+ case kMars59Retract:
+ GameState.setScoringActivatedPlatform();
+ setCurrentActivation(kActivateReactorPlatformOut);
+ _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, false);
+ break;
+ case kMars56ExtendWithBomb:
+ playSpotSoundSync(kMustBeUnlockedIn, kMustBeUnlockedOut);
+ GameState.setScoringActivatedPlatform();
+ _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true);
+ break;
+ case kMars57CantOpenPanel:
+ GameState.setScoringActivatedPlatform();
+ setCurrentActivation(kActivateReactorAskLowerScreen);
+ break;
+ case kMars57LowerScreenClosed:
+ case kMars57ThawLock:
+ setCurrentActivation(kActivateReactorReadyForNitrogen);
+ GameState.setMarsLockFrozen(false);
+ break;
+ case kMars57FreezeLock:
+ item = (InventoryItem *)_vm->getAllItems().findItemByID(kNitrogenCanister);
+ item->setItemState(kNitrogenEmpty);
+ _vm->addItemToInventory(item);
+ setCurrentActivation(kActivateReactorReadyForCrowBar);
+ GameState.setScoringUsedLiquidNitrogen();
+ GameState.setMarsLockFrozen(true);
+ showExtraView(kMars57LockFrozenView);
+ _utilityFuse.primeFuse(kLockFreezeTimeLmit);
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::lockThawed));
+ _utilityFuse.lightFuse();
+ break;
+ case kMars57BreakLock:
+ item = (InventoryItem *)_vm->getAllItems().findItemByID(kCrowbar);
+ _vm->addItemToInventory(item);
+ GameState.setScoringUsedCrowBar();
+ GameState.setMarsLockBroken(true);
+ GameState.setMarsLockFrozen(false);
+ startExtraLongSequence(kMars57OpenPanel, kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars57OpenPanel:
+ case kMars57OpenPanelChoices:
+ setCurrentActivation(kActivateReactorAskOperation);
+ break;
+ case kMars57ShieldEvaluation:
+ case kMars57MeasureOutput:
+ setCurrentActivation(kActivateReactorRanEvaluation);
+ loopExtraSequence(kMars57ShieldOkayLoop);
+ break;
+ case kMars57RunDiagnostics:
+ setCurrentActivation(kActivateReactorRanDiagnostics);
+ GameState.setScoringFoundCardBomb();
+ break;
+ case kMars57BombExplodes:
+ case kMars57BombExplodesInGame:
+ die(kDeathDidntDisarmMarsBomb);
+ break;
+ case kMars57BombAnalysis:
+ setCurrentActivation(kActivateReactorAnalyzed);
+ break;
+ case kMars57DontLink:
+ startExtraSequence(kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kMars57CircuitLink:
+ setCurrentActivation(kActivateReactorInstructions);
+ break;
+ case kMars57GameLevel1:
+ setUpReactorLevel1();
+ break;
+ case kMars57GameLevel2:
+ case kMars57GameLevel3:
+ setUpNextReactorLevel();
+ break;
+ case kMars57GameSolved:
+ setCurrentActivation(kActivateReactorBombSafe);
+ break;
+ case kMars57ExposeBomb:
+ setCurrentActivation(kActivateReactorBombExposed);
+ _privateFlags.setFlag(kMarsPrivateBombExposedFlag, true);
+ break;
+ case kMars57BackToNormal:
+ setCurrentActivation(kActivateReactorPlatformIn);
+ _privateFlags.setFlag(kMarsPrivateBombExposedFlag, false);
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM51SW", false, kWarningInterruption);
+ break;
+ case kMars60WestSpinAirlockToEast:
+ GameState.setMarsAirlockOpen(true);
+ setCurrentAlternate(kAltMars60AirlockEast);
+ turnTo(kWest);
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+ g_airMask->airQualityChanged();
+ checkAirMask();
+ loadAmbientLoops();
+ break;
+ case kMars60EastSpinAirlockToWest:
+ GameState.setMarsAirlockOpen(false);
+ setCurrentAlternate(kAltMars60AirlockWest);
+ turnTo(kEast);
+ setCurrentActivation(kActivateReadyToPressurizeAirlock);
+ g_airMask->airQualityChanged();
+ checkAirMask();
+ loadAmbientLoops();
+ break;
+ case kMarsRobotHeadOpen:
+ setCurrentActivation(kActivationRobotHeadOpen);
+ break;
+ case kMarsRobotHeadClose:
+ recallToTSASuccess();
+ break;
+ case kMarsMaze007RobotApproach:
+ case kMarsMaze015SouthRobotApproach:
+ case kMarsMaze101EastRobotApproach:
+ case kMarsMaze104WestLoop:
+ case kMarsMaze133SouthApproach:
+ case kMarsMaze136NorthApproach:
+ case kMarsMaze184WestLoop:
+ die(kDeathGroundByMazebot);
+ break;
+ }
+ } else if ((flag & kTimeForCanyonChaseFlag) != 0) {
+ doCanyonChase();
+ } else if ((flag & kExplosionFinishedFlag) != 0) {
+ _explosions.stop();
+ _explosions.hide();
+ if (g_robotShip->isDead()) {
+ GameState.setMarsFinished(true);
+ _centerShuttleMovie.hide();
+ _upperRightShuttleMovie.show();
+ _upperRightShuttleMovie.setTime(kShuttleUpperRightTargetDestroyedTime);
+ _upperRightShuttleMovie.redrawMovieWorld();
+ _rightDamageShuttleMovie.hide();
+ playMovieSegment(&_rightShuttleMovie, kShuttleRightDestroyedStart, kShuttleRightDestroyedStop);
+ playSpotSoundSync(kShuttleDestroyedIn, kShuttleDestroyedOut);
+ throwAwayMarsShuttle();
+ reinstateMonocleInterface();
+ recallToTSASuccess();
+ }
+ } else if ((flag & kTimeToTransportFlag) != 0) {
+ transportToRobotShip();
+ }
+
+ if (g_AIArea)
+ g_AIArea->checkMiddleArea();
+}
+
+void Mars::spotCompleted() {
+ Neighborhood::spotCompleted();
+
+ if (GameState.getCurrentRoom() == kMarsRobotShuttle)
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XN59WD", false, kWarningInterruption);
+}
+
+void Mars::doCanyonChase() {
+ GameState.setScoringEnteredShuttle();
+ setNextHandler(_vm);
+ throwAwayInterface();
+
+ _vm->_cursor->hide();
+
+ // Open the spot sounds movie again...
+ _spotSounds.initFromQuickTime(getSoundSpotsName());
+ _spotSounds.setVolume(_vm->getSoundFXLevel());
+
+ Video::VideoDecoder *video = new Video::QuickTimeDecoder();
+ if (!video->loadFile("Images/Mars/M44ESA.movie"))
+ error("Could not load interface->shuttle transition video");
+
+ video->start();
+
+ while (!_vm->shouldQuit() && !video->endOfVideo()) {
+ if (video->needsUpdate()) {
+ const Graphics::Surface *frame = video->decodeNextFrame();
+
+ if (frame)
+ _vm->drawScaledFrame(frame, 0, 0);
+ }
+
+ Common::Event event;
+ while (g_system->getEventManager()->pollEvent(event))
+ ;
+
+ g_system->delayMillis(10);
+ }
+
+ delete video;
+
+ if (_vm->shouldQuit())
+ return;
+
+ initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left,
+ kShuttle1Top, true);
+ initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left,
+ kShuttle2Top, true);
+ initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left,
+ kShuttle3Top, true);
+ initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left,
+ kShuttle4Top, true);
+
+ initOneMovie(&_canyonChaseMovie, "Images/Mars/Canyon.movie",
+ kShuttleMonitorOrder, kShuttleWindowLeft, kShuttleWindowTop, true);
+ _canyonChaseMovie.setVolume(_vm->getSoundFXLevel());
+
+ loadLoopSound1("Sounds/Mars/Inside Cockpit.22K.8.AIFF");
+
+ // Swing shuttle around...
+ playMovieSegment(&_canyonChaseMovie, kShuttleSwingStart, kShuttleSwingStop);
+
+ initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false);
+
+ initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false);
+
+ initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false);
+
+ initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleLowerRightLeft, kShuttleLowerRightTop, false);
+
+ initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false);
+
+ initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false);
+
+ initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleUpperRightLeft, kShuttleUpperRightTop, false);
+
+ initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie",
+ kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false);
+
+ initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie",
+ kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false);
+
+ _centerShuttleMovie.show();
+ _centerShuttleMovie.setTime(kShuttleCenterBoardingTime);
+ playSpotSoundSync(kShuttleCockpitIn, kShuttleCockpitOut);
+
+ _centerShuttleMovie.setTime(kShuttleCenterCheckTime);
+ playSpotSoundSync(kShuttleOnboardIn, kShuttleOnboardOut);
+
+ _shuttleEnergyMeter.initShuttleEnergyMeter();
+ _shuttleEnergyMeter.powerUpMeter();
+ while (_shuttleEnergyMeter.isFading()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ g_system->updateScreen();
+ }
+
+ _leftShuttleMovie.show();
+ playMovieSegment(&_leftShuttleMovie, kShuttleLeftIntroStart, kShuttleLeftIntroStop);
+
+ _leftShuttleMovie.setTime(kShuttleLeftNormalTime);
+ _leftShuttleMovie.redrawMovieWorld();
+
+ _leftDamageShuttleMovie.show();
+ playMovieSegment(&_leftDamageShuttleMovie);
+
+ // Take it down a tick initially. This sets the time to the time of the last tick,
+ // so that subsequence drops will drop it down a tick.
+ _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getTime() - 40);
+ _leftDamageShuttleMovie.redrawMovieWorld();
+
+ _lowerRightShuttleMovie.show();
+ _lowerRightShuttleMovie.setTime(kShuttleLowerRightOffTime);
+ _lowerRightShuttleMovie.redrawMovieWorld();
+ _centerShuttleMovie.setTime(kShuttleCenterNavCompTime);
+ _centerShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kShuttleNavigationIn, kShuttleNavigationOut);
+
+ _centerShuttleMovie.setTime(kShuttleCenterCommTime);
+ _centerShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kShuttleCommunicationIn, kShuttleCommunicationOut);
+
+ _centerShuttleMovie.setTime(kShuttleCenterAllSystemsTime);
+ _centerShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kShuttleAllSystemsIn, kShuttleAllSystemsOut);
+
+ _centerShuttleMovie.setTime(kShuttleCenterSecureLooseTime);
+ _centerShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kShuttleSecureLooseIn, kShuttleSecureLooseOut);
+
+ _centerShuttleMovie.setTime(kShuttleCenterAutoTestTime);
+ _centerShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kShuttleAutoTestingIn, kShuttleAutoTestingOut);
+
+ _leftShuttleMovie.setTime(kShuttleLeftAutoTestTime);
+ _leftShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kMarsThrusterAutoTestIn, kMarsThrusterAutoTestOut);
+ _leftShuttleMovie.setTime(kShuttleLeftNormalTime);
+ _leftShuttleMovie.redrawMovieWorld();
+
+ _centerShuttleMovie.setTime(kShuttleCenterLaunchTime);
+ _centerShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kShuttlePrepareForDropIn, kShuttlePrepareForDropOut);
+
+ playSpotSoundSync(kShuttleAllClearIn, kShuttleAllClearOut);
+
+ _centerShuttleMovie.setTime(kShuttleCenterEnterTubeTime);
+ _centerShuttleMovie.redrawMovieWorld();
+
+ _lowerLeftShuttleMovie.show();
+ _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftCollisionTime);
+
+ loadLoopSound1("");
+
+ _canyonChaseMovie.setSegment(kCanyonChaseStart, kCanyonChaseStop);
+ _canyonChaseMovie.start();
+
+ startMarsTimer(kLaunchTubeReachedTime, kMovieTicksPerSecond, kMarsLaunchTubeReached);
+}
+
+void Mars::startUpFromFinishedSpaceChase() {
+ setNextHandler(_vm);
+ throwAwayInterface();
+
+ initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left,
+ kShuttle1Top, true);
+ initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left,
+ kShuttle2Top, true);
+ initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left,
+ kShuttle3Top, true);
+ initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left,
+ kShuttle4Top, true);
+
+ initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false);
+
+ initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false);
+
+ initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false);
+
+ initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleLowerRightLeft, kShuttleLowerRightTop, false);
+
+ initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false);
+
+ initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false);
+
+ initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleUpperRightLeft, kShuttleUpperRightTop, false);
+
+ initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie",
+ kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false);
+
+ initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie",
+ kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false);
+
+ _centerShuttleMovie.show();
+
+ _shuttleEnergyMeter.initShuttleEnergyMeter();
+ _shuttleEnergyMeter.setEnergyValue(kFullShuttleEnergy);
+
+ _leftShuttleMovie.show();
+ _leftShuttleMovie.setTime(kShuttleLeftNormalTime);
+ _leftShuttleMovie.redrawMovieWorld();
+
+ _leftDamageShuttleMovie.show();
+ _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getDuration() - 40);
+ _leftDamageShuttleMovie.redrawMovieWorld();
+
+ _lowerRightShuttleMovie.show();
+
+ _lowerLeftShuttleMovie.show();
+
+ loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF");
+
+ initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft,
+ kShuttleJunkTop, false);
+
+ initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false);
+ _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes);
+
+ _energyBeam.initShuttleWeapon();
+ _gravitonCannon.initShuttleWeapon();
+
+ _upperLeftShuttleMovie.show();
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+
+ _rightShuttleMovie.show();
+ _rightShuttleMovie.setTime(kShuttleRightIntroStop - 1);
+ _rightShuttleMovie.redrawMovieWorld();
+
+ _rightDamageShuttleMovie.show();
+ _rightDamageShuttleMovie.setTime(40);
+ _rightDamageShuttleMovie.redrawMovieWorld();
+
+ _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime);
+ _lowerLeftShuttleMovie.redrawMovieWorld();
+
+ _shuttleTransportSpot.setArea(kShuttleTransportBounds);
+ _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_shuttleTransportSpot);
+
+ _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true);
+
+ _upperRightShuttleMovie.show();
+ _upperRightShuttleMovie.setTime(kShuttleUpperRightOverloadTime);
+ _upperRightShuttleMovie.redrawMovieWorld();
+
+ _centerShuttleMovie.setTime(kShuttleCenterSafeTime);
+ _centerShuttleMovie.redrawMovieWorld();
+
+ _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportTime);
+ _lowerRightShuttleMovie.redrawMovieWorld();
+
+ initOneMovie(&_canyonChaseMovie, "Images/Mars/M98EAS.movie", kShuttleTractorBeamMovieOrder,
+ kShuttleWindowLeft, kShuttleWindowTop, true);
+ _canyonChaseMovie.setTime(_canyonChaseMovie.getDuration());
+ _canyonChaseMovie.redrawMovieWorld();
+}
+
+void Mars::startUpFromSpaceChase() {
+ setNextHandler(_vm);
+ throwAwayInterface();
+
+ // Open the spot sounds movie again...
+ _spotSounds.initFromQuickTime(getSoundSpotsName());
+ _spotSounds.setVolume(_vm->getSoundFXLevel());;
+
+ initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left,
+ kShuttle1Top, true);
+ initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left,
+ kShuttle2Top, true);
+ initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left,
+ kShuttle3Top, true);
+ initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left,
+ kShuttle4Top, true);
+
+ initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false);
+
+ initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false);
+
+ initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false);
+
+ initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleLowerRightLeft, kShuttleLowerRightTop, false);
+
+ initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie",
+ kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false);
+
+ initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false);
+
+ initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder,
+ kShuttleUpperRightLeft, kShuttleUpperRightTop, false);
+
+ initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie",
+ kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false);
+
+ initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie",
+ kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false);
+
+ _centerShuttleMovie.show();
+
+ _shuttleEnergyMeter.initShuttleEnergyMeter();
+ _shuttleEnergyMeter.setEnergyValue(kFullShuttleEnergy);
+
+ _leftShuttleMovie.show();
+ _leftShuttleMovie.setTime(kShuttleLeftNormalTime);
+ _leftShuttleMovie.redrawMovieWorld();
+
+ _leftDamageShuttleMovie.show();
+ _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getDuration() - 40);
+ _leftDamageShuttleMovie.redrawMovieWorld();
+
+ _lowerRightShuttleMovie.show();
+
+ _lowerLeftShuttleMovie.show();
+
+ loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF");
+
+ initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder,
+ kPlanetStartLeft, kPlanetStartTop, true);
+ _planetMovie.setFlags(kLoopTimeBase);
+
+ initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft,
+ kShuttleJunkTop, false);
+
+ initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false);
+ _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes);
+
+ _energyBeam.initShuttleWeapon();
+ _gravitonCannon.initShuttleWeapon();
+
+ _upperLeftShuttleMovie.show();
+
+ _robotShip.initRobotShip();
+
+ _planetMovie.start();
+ _planetMover.startMoving(&_planetMovie);
+
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+
+ _centerShuttleMovie.setTime(kShuttleCenterTargetSightedTime);
+ _centerShuttleMovie.redrawMovieWorld();
+
+ _lowerRightShuttleMovie.setTime(kShuttleLowerRightTrackingTime);
+ _lowerRightShuttleMovie.redrawMovieWorld();
+
+ _rightShuttleMovie.show();
+ _rightShuttleMovie.setTime(kShuttleRightIntroStop - 1);
+ _rightShuttleMovie.redrawMovieWorld();
+
+ _rightDamageShuttleMovie.show();
+ _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getDuration() - 40);
+ _rightDamageShuttleMovie.redrawMovieWorld();
+
+ _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime);
+ _lowerLeftShuttleMovie.redrawMovieWorld();
+
+ _robotShip.startMoving();
+
+ _shuttleHUD.initShuttleHUD();
+
+ _tractorBeam.startDisplaying();
+
+ _energyChoiceSpot.setArea(kShuttleEnergyBeamBounds);
+ _energyChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_energyChoiceSpot);
+ _gravitonChoiceSpot.setArea(kShuttleGravitonBounds);
+ _gravitonChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_gravitonChoiceSpot);
+ _tractorChoiceSpot.setArea(kShuttleTractorBounds);
+ _tractorChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_tractorChoiceSpot);
+ _shuttleViewSpot.setArea(kShuttleWindowLeft, kShuttleWindowTop,
+ kShuttleWindowLeft + kShuttleWindowWidth, kShuttleWindowTop + kShuttleWindowHeight);
+ _shuttleViewSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_shuttleViewSpot);
+ _shuttleTransportSpot.setArea(kShuttleTransportBounds);
+ _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_shuttleTransportSpot);
+
+ _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true);
+
+ startMarsTimer(kSpaceChaseTimeLimit, kOneTickPerSecond, kMarsSpaceChaseFinished);
+}
+
+void Mars::setSoundFXLevel(const uint16 level) {
+ Neighborhood::setSoundFXLevel(level);
+
+ if (_canyonChaseMovie.isMovieValid())
+ _canyonChaseMovie.setVolume(level);
+
+ if (_explosions.isMovieValid())
+ _explosions.setVolume(level);
+}
+
+void Mars::startMarsTimer(TimeValue time, TimeScale scale, MarsTimerCode code) {
+ _utilityFuse.primeFuse(time, scale);
+ _marsEvent.mars = this;
+ _marsEvent.event = code;
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, MarsTimerEvent>(&_marsEvent, &MarsTimerEvent::fire));
+ _utilityFuse.lightFuse();
+}
+
+void Mars::marsTimerExpired(MarsTimerEvent &event) {
+ Common::Rect r;
+ uint16 x, y;
+
+ switch (event.event) {
+ case kMarsLaunchTubeReached:
+ _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftTubeTime);
+ _lowerLeftShuttleMovie.redrawMovieWorld();
+ startMarsTimer(kCanyonChaseFinishedTime, kMovieTicksPerSecond, kMarsCanyonChaseFinished);
+ break;
+ case kMarsCanyonChaseFinished:
+ GameState.setScoringEnteredLaunchTube();
+
+ while (_canyonChaseMovie.isRunning()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+
+ _canyonChaseMovie.stop();
+ _canyonChaseMovie.stopDisplaying();
+ _canyonChaseMovie.releaseMovie();
+
+ _vm->_gfx->enableErase();
+
+ loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF");
+
+ playSpotSoundSync(kShuttleConfiguringIn, kShuttleConfiguringOut);
+ playSpotSoundSync(kShuttleGeneratingIn, kShuttleGeneratingOut);
+ playSpotSoundSync(kShuttleBreakawayIn, kShuttleBreakawayOut);
+ playSpotSoundSync(kMarsAtmosphericBreakawayIn, kMarsAtmosphericBreakawayOut);
+
+ initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder, kPlanetStartLeft, kPlanetStartTop, true);
+ _planetMovie.setFlags(kLoopTimeBase);
+
+ initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, kShuttleJunkTop, false);
+
+ initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false);
+ _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes);
+
+ _energyBeam.initShuttleWeapon();
+ _gravitonCannon.initShuttleWeapon();
+
+ _centerShuttleMovie.setTime(kShuttleCenterWeaponsTime);
+ _centerShuttleMovie.redrawMovieWorld();
+
+ _upperLeftShuttleMovie.show();
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDampingTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+
+ _robotShip.initRobotShip();
+
+ _planetMovie.start();
+ _planetMover.startMoving(&_planetMovie);
+
+ playSpotSoundSync(kShuttleDamperDescIn, kShuttleDamperDescOut);
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftGravitonTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+
+ playSpotSoundSync(kShuttleGravitonDescIn, kShuttleGravitonDescOut);
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftTractorTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+
+ playSpotSoundSync(kShuttleTractorDescIn, kShuttleTractorDescOut);
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+
+ _centerShuttleMovie.setTime(kShuttleCenterTargetSightedTime);
+ _centerShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kShuttleTargetSightedIn, kShuttleTargetSightedOut);
+
+ _lowerRightShuttleMovie.setTime(kShuttleLowerRightTrackingTime);
+ _lowerRightShuttleMovie.redrawMovieWorld();
+ _rightShuttleMovie.show();
+ playMovieSegment(&_rightShuttleMovie, kShuttleRightIntroStart, kShuttleRightIntroStop);
+
+ _rightDamageShuttleMovie.show();
+ playMovieSegment(&_rightDamageShuttleMovie);
+
+ // Take it down a tick initially. This sets the time to the time of the last tick,
+ // so that subsequence drops will drop it down a tick.
+ _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getTime() - 40);
+ _rightDamageShuttleMovie.redrawMovieWorld();
+
+ _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime);
+ _lowerLeftShuttleMovie.redrawMovieWorld();
+ playSpotSoundSync(kShuttleAutopilotEngagedIn, kShuttleAutopilotEngagedOut);
+
+ _robotShip.startMoving();
+
+ _shuttleHUD.initShuttleHUD();
+
+ _tractorBeam.startDisplaying();
+
+ _energyChoiceSpot.setArea(kShuttleEnergyBeamBounds);
+ _energyChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_energyChoiceSpot);
+ _gravitonChoiceSpot.setArea(kShuttleGravitonBounds);
+ _gravitonChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_gravitonChoiceSpot);
+ _tractorChoiceSpot.setArea(kShuttleTractorBounds);
+ _tractorChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_tractorChoiceSpot);
+ _shuttleViewSpot.setArea(kShuttleWindowLeft, kShuttleWindowTop,
+ kShuttleWindowLeft + kShuttleWindowWidth, kShuttleWindowTop + kShuttleWindowHeight);
+ _shuttleViewSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_shuttleViewSpot);
+ _shuttleTransportSpot.setArea(kShuttleTransportBounds);
+ _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+ _vm->getAllHotspots().push_back(&_shuttleTransportSpot);
+
+ _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true);
+
+ playSpotSoundSync(kMarsCockpitChatterIn, kMarsCockpitChatterOut);
+
+ GameState.setMarsFinishedCanyonChase(true);
+
+ startMarsTimer(kSpaceChaseTimeLimit, kOneTickPerSecond, kMarsSpaceChaseFinished);
+
+ _vm->_cursor->hideUntilMoved();
+ break;
+ case kMarsSpaceChaseFinished:
+ // Player failed to stop the robot in time...
+ _interruptionFilter = kFilterNoInput;
+
+ _rightShuttleMovie.setTime(kShuttleRightTargetLockTime);
+ _rightShuttleMovie.redrawMovieWorld();
+
+ _upperRightShuttleMovie.show();
+ _upperRightShuttleMovie.setTime(kShuttleUpperRightLockedTime);
+ _upperRightShuttleMovie.redrawMovieWorld();
+
+ _rightShuttleMovie.setTime(kShuttleRightGravitonTime);
+ _rightShuttleMovie.redrawMovieWorld();
+ _upperRightShuttleMovie.setTime(kShuttleUpperRightArmedTime);
+ _upperRightShuttleMovie.redrawMovieWorld();
+
+ _vm->delayShell(3, 1);
+
+ x = _vm->getRandomNumber(19);
+ y = _vm->getRandomNumber(19);
+
+ r = Common::Rect(kShuttleWindowMidH - x, kShuttleWindowMidV - y,
+ kShuttleWindowMidH - x + 20, kShuttleWindowMidV - y + 20);
+ showBigExplosion(r, kShuttleAlienShipOrder);
+
+ while (_explosions.isRunning()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ g_system->delayMillis(10);
+ }
+
+ throwAwayMarsShuttle();
+ reinstateMonocleInterface();
+ recallToTSAFailure();
+ break;
+ default:
+ break;
+ }
+
+ _interruptionFilter = kFilterAllInput;
+}
+
+void Mars::throwAwayMarsShuttle() {
+ _shuttleInterface1.deallocateSurface();
+ _shuttleInterface1.stopDisplaying();
+ _shuttleInterface2.deallocateSurface();
+ _shuttleInterface2.stopDisplaying();
+ _shuttleInterface3.deallocateSurface();
+ _shuttleInterface3.stopDisplaying();
+ _shuttleInterface4.deallocateSurface();
+ _shuttleInterface4.stopDisplaying();
+
+ _spotSounds.disposeSound();
+
+ _canyonChaseMovie.releaseMovie();
+ _canyonChaseMovie.stopDisplaying();
+ _leftShuttleMovie.releaseMovie();
+ _leftShuttleMovie.stopDisplaying();
+ _rightShuttleMovie.releaseMovie();
+ _rightShuttleMovie.stopDisplaying();
+ _lowerLeftShuttleMovie.releaseMovie();
+ _lowerLeftShuttleMovie.stopDisplaying();
+ _lowerRightShuttleMovie.releaseMovie();
+ _lowerRightShuttleMovie.stopDisplaying();
+ _centerShuttleMovie.releaseMovie();
+ _centerShuttleMovie.stopDisplaying();
+ _upperLeftShuttleMovie.releaseMovie();
+ _upperLeftShuttleMovie.stopDisplaying();
+ _upperRightShuttleMovie.releaseMovie();
+ _upperRightShuttleMovie.stopDisplaying();
+ _leftDamageShuttleMovie.releaseMovie();
+ _leftDamageShuttleMovie.stopDisplaying();
+ _rightDamageShuttleMovie.releaseMovie();
+ _rightDamageShuttleMovie.stopDisplaying();
+
+ _shuttleEnergyMeter.disposeShuttleEnergyMeter();
+ _robotShip.cleanUpRobotShip();
+ _shuttleHUD.cleanUpShuttleHUD();
+ _tractorBeam.stopDisplaying();
+ _junk.releaseMovie();
+ _junk.stopDisplaying();
+ _energyBeam.cleanUpShuttleWeapon();
+ _gravitonCannon.cleanUpShuttleWeapon();
+ _vm->getAllHotspots().remove(&_energyChoiceSpot);
+ _vm->getAllHotspots().remove(&_gravitonChoiceSpot);
+ _vm->getAllHotspots().remove(&_tractorChoiceSpot);
+ _vm->getAllHotspots().remove(&_shuttleViewSpot);
+ _vm->getAllHotspots().remove(&_shuttleTransportSpot);
+ _explosions.releaseMovie();
+ _explosions.stopDisplaying();
+
+ loadLoopSound1("");
+}
+
+void Mars::transportToRobotShip() {
+ throwAwayMarsShuttle();
+
+ Video::VideoDecoder *video = new Video::QuickTimeDecoder();
+ if (!video->loadFile("Images/Mars/M98EAE.movie"))
+ error("Could not load shuttle->interface transition video");
+
+ video->start();
+
+ while (!_vm->shouldQuit() && !video->endOfVideo()) {
+ if (video->needsUpdate()) {
+ const Graphics::Surface *frame = video->decodeNextFrame();
+
+ if (frame)
+ _vm->drawScaledFrame(frame, 0, 0);
+ }
+
+ Common::Event event;
+ while (g_system->getEventManager()->pollEvent(event))
+ ;
+
+ g_system->delayMillis(10);
+ }
+
+ delete video;
+
+ if (_vm->shouldQuit())
+ return;
+
+ reinstateMonocleInterface();
+
+ g_energyMonitor->stopEnergyDraining();
+ g_energyMonitor->restoreLastEnergyValue();
+ _vm->resetEnergyDeathReason();
+ g_energyMonitor->startEnergyDraining();
+
+ arriveAt(kMarsRobotShuttle, kEast);
+
+ _navMovie.stop();
+ _navMovie.setTime(_navMovie.getStart());
+ _navMovie.start();
+}
+
+const int kRobotTooStrong = 1;
+const int kTractorTooWeak = 2;
+const int kCapturedRobotShip = 3;
+
+void Mars::spaceChaseClick(const Input &input, const HotSpotID id) {
+ Common::Point pt;
+
+ switch (id) {
+ case kShuttleEnergySpotID:
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDampingTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+ _leftShuttleMovie.setTime(kShuttleLeftDampingTime);
+ _leftShuttleMovie.redrawMovieWorld();
+ _shuttleHUD.hide();
+ _weaponSelection = kEnergyBeam;
+ playSpotSoundSync(kShuttleDampingBeamIn, kShuttleDampingBeamOut);
+ break;
+ case kShuttleGravitonSpotID:
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftGravitonTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+ _leftShuttleMovie.setTime(kShuttleLeftGravitonTime);
+ _leftShuttleMovie.redrawMovieWorld();
+ _shuttleHUD.hide();
+ _weaponSelection = kGravitonCannon;
+ playSpotSoundSync(kShuttleGravitonIn, kShuttleGravitonOut);
+ break;
+ case kShuttleTractorSpotID:
+ _upperLeftShuttleMovie.setTime(kShuttleUpperLeftTractorTime);
+ _upperLeftShuttleMovie.redrawMovieWorld();
+ _leftShuttleMovie.setTime(kShuttleLeftTractorTime);
+ _leftShuttleMovie.redrawMovieWorld();
+ _shuttleHUD.show();
+ _weaponSelection = kTractorBeam;
+ playSpotSoundSync(kShuttleTractorBeamIn, kShuttleTractorBeamOut);
+ break;
+ case kShuttleViewSpotID:
+ switch (_weaponSelection) {
+ case kEnergyBeam:
+ if (_shuttleEnergyMeter.getEnergyValue() < kMinDampingEnergy) {
+ playSpotSoundSync(kShuttleEnergyTooLowIn, kShuttleEnergyTooLowOut);
+ } else {
+ if (_energyBeam.canFireWeapon()) {
+ _shuttleEnergyMeter.dropEnergyValue(kMinDampingEnergy);
+ input.getInputLocation(pt);
+ _energyBeam.fireWeapon(pt.x, pt.y);
+ playSpotSoundSync(kMarsEDBBlastIn, kMarsEDBBlastOut);
+ }
+ }
+ break;
+ case kGravitonCannon:
+ if (_shuttleEnergyMeter.getEnergyValue() < kMinGravitonEnergy) {
+ playSpotSoundSync(kShuttleEnergyTooLowIn, kShuttleEnergyTooLowOut);
+ } else {
+ if (_gravitonCannon.canFireWeapon()) {
+ _shuttleEnergyMeter.dropEnergyValue(kMinGravitonEnergy);
+ input.getInputLocation(pt);
+ _gravitonCannon.fireWeapon(pt.x, pt.y);
+ playSpotSoundSync(kMarsGravitonBlastIn, kMarsGravitonBlastOut);
+ }
+ }
+ break;
+ case kTractorBeam:
+ if (_shuttleHUD.isTargetLocked()) {
+ // play tractor beam sound?
+ _utilityFuse.stopFuse();
+
+ _tractorBeam.show();
+
+ int capture;
+ if (_rightDamageShuttleMovie.getTime() > 40) {
+ capture = kRobotTooStrong;
+ } else if (!_shuttleEnergyMeter.enoughEnergyForTractorBeam()) {
+ capture = kTractorTooWeak;
+ } else {
+ _robotShip.snareByTractorBeam();
+ capture = kCapturedRobotShip;
+ _planetMover.dropPlanetOutOfSight();
+ }
+
+ _shuttleEnergyMeter.drainForTractorBeam();
+
+ while (_shuttleEnergyMeter.isFading()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+
+ _shuttleEnergyMeter.setEnergyValue(_shuttleEnergyMeter.getEnergyValue());
+
+ switch (capture) {
+ case kRobotTooStrong:
+ _tractorBeam.hide();
+ playSpotSoundSync(kShuttleBrokeFreeIn, kShuttleBrokeFreeOut);
+ _utilityFuse.lightFuse();
+ break;
+ case kTractorTooWeak:
+ playSpotSoundSync(kShuttleCantHoldIn, kShuttleCantHoldOut);
+ _tractorBeam.hide();
+ _utilityFuse.lightFuse();
+ break;
+ case kCapturedRobotShip:
+ _tractorBeam.hide();
+ _shuttleHUD.hide();
+ _robotShip.cleanUpRobotShip();
+ _planetMovie.stop();
+ _planetMovie.stopDisplaying();
+ _planetMovie.releaseMovie();
+
+ // Shameless reuse of a variable :P
+ initOneMovie(&_canyonChaseMovie, "Images/Mars/M98EAS.movie", kShuttleTractorBeamMovieOrder,
+ kShuttleWindowLeft, kShuttleWindowTop, true);
+ _canyonChaseMovie.redrawMovieWorld();
+ playMovieSegment(&_canyonChaseMovie, 0, _canyonChaseMovie.getDuration());
+
+ // wait here until any junk clears...
+ while (_junk.junkFlying()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+
+ _upperRightShuttleMovie.show();
+ _upperRightShuttleMovie.setTime(kShuttleUpperRightOverloadTime);
+ _upperRightShuttleMovie.redrawMovieWorld();
+
+ playSpotSoundSync(kShuttleOverloadedIn, kShuttleOverloadedOut);
+ _centerShuttleMovie.setTime(kShuttleCenterVerifyingTime);
+ _centerShuttleMovie.redrawMovieWorld();
+
+ playSpotSoundSync(kShuttleCoordinatesIn, kShuttleCoordinatesOut);
+ _centerShuttleMovie.setTime(kShuttleCenterScanningTime);
+ _centerShuttleMovie.redrawMovieWorld();
+
+ playSpotSoundSync(kShuttleScanningIn, kShuttleScanningOut);
+ _centerShuttleMovie.setTime(kShuttleCenterSafeTime);
+ _centerShuttleMovie.redrawMovieWorld();
+
+ playSpotSoundSync(kShuttleSafeIn, kShuttleSafeOut);
+ _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportTime);
+ _lowerRightShuttleMovie.redrawMovieWorld();
+ GameState.setMarsReadyForShuttleTransport(true);
+ break;
+ }
+ } else {
+ playSpotSoundSync(kShuttleTractorLimitedIn, kShuttleTractorLimitedOut);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case kShuttleTransportSpotID:
+ _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportHiliteTime);
+ _lowerRightShuttleMovie.redrawMovieWorld();
+ _neighborhoodNotification.setNotificationFlags(kTimeToTransportFlag, kTimeToTransportFlag);
+ break;
+ }
+}
+
+void Mars::showBigExplosion(const Common::Rect &r, const DisplayOrder order) {
+ if (_explosions.isMovieValid()) {
+ _explosions.setDisplayOrder(order);
+
+ Common::Rect r2 = r;
+ int dx = r.width() / 2;
+ int dy = r.height() / 2;
+ r2.left -= dx;
+ r2.right += dx;
+ r2.top -= dy;
+ r2.bottom += dy;
+
+ _explosions.setBounds(r2);
+ _explosions.show();
+ _explosions.stop();
+ _explosions.setSegment(kBigExplosionStart, kBigExplosionStop);
+ _explosions.setTime(kBigExplosionStart);
+ _explosionCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _explosions.start();
+ }
+}
+
+void Mars::showLittleExplosion(const Common::Rect &r, const DisplayOrder order) {
+ if (_explosions.isMovieValid()) {
+ _explosions.setDisplayOrder(order);
+
+ Common::Rect r2 = r;
+ int dx = r.width() / 2;
+ int dy = r.height() / 2;
+ r2.left -= dx;
+ r2.right += dx;
+ r2.top -= dy;
+ r2.bottom += dy;
+ _explosions.setBounds(r2);
+
+ _explosions.show();
+ _explosions.stop();
+ _explosions.setSegment(kLittleExplosionStart, kLittleExplosionStop);
+ _explosions.setTime(kLittleExplosionStart);
+ _explosionCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _explosions.start();
+ }
+}
+
+void Mars::hitByJunk() {
+ _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getTime() - 40);
+ _leftDamageShuttleMovie.redrawMovieWorld();
+
+ playSpotSoundSync(kMarsJunkCollisionIn, kMarsJunkCollisionOut);
+
+ if (_leftDamageShuttleMovie.getTime() == 0) {
+ die(kDeathRanIntoSpaceJunk);
+ } else {
+ TimeValue t = _leftDamageShuttleMovie.getTime() / 40;
+
+ if (t == 1)
+ playSpotSoundSync(kShuttleHullBreachIn, kShuttleHullBreachOut);
+
+ t = _leftShuttleMovie.getTime();
+ _leftShuttleMovie.setTime(kShuttleLeftDamagedTime);
+ _leftShuttleMovie.redrawMovieWorld();
+ _vm->delayShell(1, 3);
+ _leftShuttleMovie.setTime(t);
+ _leftShuttleMovie.redrawMovieWorld();
+ }
+}
+
+void Mars::setUpNextDropTime() {
+ _robotShip.setUpNextDropTime();
+}
+
+void Mars::decreaseRobotShuttleEnergy(const int delta, Common::Point impactPoint) {
+ _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getTime() - 40 * delta);
+ _rightDamageShuttleMovie.redrawMovieWorld();
+
+ if (_rightDamageShuttleMovie.getTime() == 0) {
+ Common::Rect r;
+ _robotShip.getShuttleBounds(r);
+ int size = MAX(r.width(), r.height());
+ r = Common::Rect::center(impactPoint.x, impactPoint.y, size, size);
+ _robotShip.killRobotShip();
+ showBigExplosion(r, kShuttleRobotShipOrder);
+ } else if (delta > 1) {
+ Common::Rect r;
+ _robotShip.getShuttleBounds(r);
+ int size = MIN(r.width(), r.height());
+ r = Common::Rect::center(impactPoint.x, impactPoint.y, size, size);
+ showLittleExplosion(r, kShuttleWeaponBackOrder);
+ TimeValue t = _rightShuttleMovie.getTime();
+ _rightShuttleMovie.setTime(kShuttleRightDamagedTime);
+ _rightShuttleMovie.redrawMovieWorld();
+ _vm->delayShell(1, 3);
+ _rightShuttleMovie.setTime(t);
+ _rightShuttleMovie.redrawMovieWorld();
+ }
+
+ if (_rightDamageShuttleMovie.getTime() <= 40) {
+ GameState.setScoringStoppedRobotsShuttle();
+ if (!GameState.getMarsHitRobotWithCannon())
+ GameState.setScoringMarsGandhi();
+ }
+}
+
+void Mars::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) {
+ if (cursorSpot && cursorSpot->getObjectID() == kShuttleViewSpotID) {
+ if (_weaponSelection != kNoWeapon)
+ _vm->_cursor->setCurrentFrameIndex(6);
+ else
+ _vm->_cursor->setCurrentFrameIndex(0);
+ } else {
+ Neighborhood::updateCursor(cursorLocation, cursorSpot);
+ }
+}
+
+AirQuality Mars::getAirQuality(const RoomID room) {
+ if ((room >= kMars36 && room <= kMars39) || (room >= kMarsMaze004 && room <= kMarsMaze200))
+ return kAirQualityVacuum;
+ if (room == kMars35 && !GameState.getMarsAirlockOpen())
+ return kAirQualityVacuum;
+ if (room == kMars60 && !GameState.getMarsAirlockOpen())
+ return kAirQualityVacuum;
+
+ return Neighborhood::getAirQuality(room);
+}
+
+// Start up panting sound if necessary.
+
+void Mars::checkAirMask() {
+ Neighborhood::checkAirMask();
+
+ if (getAirQuality(GameState.getCurrentRoom()) == kAirQualityVacuum) {
+ if (g_airMask->isAirMaskOn()) {
+ if (_noAirFuse.isFuseLit()) {
+ _noAirFuse.stopFuse();
+ loadLoopSound2("");
+ loadAmbientLoops();
+ playSpotSoundSync(kMarsOxyMaskOnIn, kMarsOxyMaskOnOut);
+ }
+ } else {
+ if (!_noAirFuse.isFuseLit()) {
+ loadLoopSound2("Sounds/Mars/SukWind1.22K.AIFF");
+ _noAirFuse.primeFuse(kVacuumSurvivalTimeLimit);
+ _noAirFuse.lightFuse();
+ }
+ }
+ } else {
+ if (_noAirFuse.isFuseLit()) {
+ _noAirFuse.stopFuse();
+ loadLoopSound2("");
+ loadAmbientLoops();
+ }
+ }
+}
+
+void Mars::airStageExpired() {
+ if (((PegasusEngine *)g_engine)->playerHasItemID(kAirMask))
+ die(kDeathNoAirInMaze);
+ else
+ die(kDeathNoMaskInMaze);
+}
+
+void Mars::lockThawed() {
+ startExtraSequence(kMars57ThawLock, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void Mars::setUpReactorLevel1() {
+ _reactorStage = 1;
+ makeColorSequence();
+ _guessObject.initReactorGuess();
+ _undoPict.initFromPICTResource(_vm->_resFork, kReactorUndoHilitePICTID);
+ _undoPict.setDisplayOrder(kMonitorLayer);
+ _undoPict.moveElementTo(kUndoHiliteLeft, kUndoHiliteTop);
+ _undoPict.startDisplaying();
+ _guessHistory.initReactorHistory();
+ _choiceHighlight.initReactorChoiceHighlight();
+ setCurrentActivation(kActivateReactorInGame);
+ _bombFuse.primeFuse(kColorMatchingTimeLimit);
+ _bombFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::bombExplodesInGame));
+ _bombFuse.lightFuse();
+}
+
+void Mars::setUpNextReactorLevel() {
+ _guessObject.show();
+ _guessHistory.show();
+ _guessHistory.clearHistory();
+ _choiceHighlight.show();
+ _reactorStage++;
+ makeColorSequence();
+}
+
+void Mars::makeColorSequence() {
+ int32 code[5];
+ int32 highest = _reactorStage + 2;
+
+ for (int32 i = 0; i < highest; i++)
+ code[i] = i;
+
+ _vm->shuffleArray(code, highest);
+ _currentGuess[0] = -1;
+ _currentGuess[1] = -1;
+ _currentGuess[2] = -1;
+ _nextGuess = 0;
+ _guessObject.setGuess(-1, -1, -1);
+ _guessHistory.setAnswer(code[0], code[1], code[2]);
+}
+
+void Mars::doUndoOneGuess() {
+ if (_nextGuess > 0) {
+ _undoPict.show();
+ _vm->delayShell(1, 2);
+ _undoPict.hide();
+ _nextGuess--;
+ _currentGuess[_nextGuess] = -1;
+ _guessObject.setGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]);
+ _choiceHighlight.resetHighlight();
+
+ if (_currentGuess[0] != -1) {
+ _choiceHighlight.highlightChoice(_currentGuess[0]);
+
+ if (_currentGuess[1] != -1) {
+ _choiceHighlight.highlightChoice(_currentGuess[1]);
+
+ if (_currentGuess[2] != -1)
+ _choiceHighlight.highlightChoice(_currentGuess[2]);
+ }
+ }
+ }
+}
+
+void Mars::doReactorGuess(int32 guess) {
+ _choiceHighlight.highlightChoice(guess);
+ _currentGuess[_nextGuess] = guess;
+ _guessObject.setGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]);
+
+ switch (guess) {
+ case 0:
+ playSpotSoundSync(kColorMatchRedIn, kColorMatchRedOut);
+ break;
+ case 1:
+ playSpotSoundSync(kColorMatchYellowIn, kColorMatchYellowOut);
+ break;
+ case 2:
+ playSpotSoundSync(kColorMatchGreenIn, kColorMatchGreenOut);
+ break;
+ case 3:
+ playSpotSoundSync(kColorMatchBlueIn, kColorMatchBlueOut);
+ break;
+ case 4:
+ playSpotSoundSync(kColorMatchPurpleIn, kColorMatchPurpleOut);
+ break;
+ }
+
+ _nextGuess++;
+
+ if (_nextGuess == 3) {
+ _vm->delayShell(1, 2);
+ _nextGuess = 0;
+ _guessHistory.addGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]);
+
+ switch (_guessHistory.getCurrentNumCorrect()) {
+ case 0:
+ playSpotSoundSync(kColorMatchZeroNodesIn, kColorMatchZeroNodesOut);
+ break;
+ case 1:
+ playSpotSoundSync(kColorMatchOneNodeIn, kColorMatchOneNodeOut);
+ break;
+ case 2:
+ playSpotSoundSync(kColorMatchTwoNodesIn, kColorMatchTwoNodesOut);
+ break;
+ case 3:
+ playSpotSoundSync(kColorMatchThreeNodesIn, kColorMatchThreeNodesOut);
+ break;
+ }
+
+ _currentGuess[0] = -1;
+ _currentGuess[1] = -1;
+ _currentGuess[2] = -1;
+ _guessObject.setGuess(-1, -1, -1);
+ _choiceHighlight.resetHighlight();
+
+ if (_guessHistory.isSolved()) {
+ _guessHistory.showAnswer();
+ _vm->delayShell(1, 2);
+ _guessObject.hide();
+ _guessHistory.hide();
+ _choiceHighlight.hide();
+
+ switch (_reactorStage) {
+ case 1:
+ startExtraSequence(kMars57GameLevel2, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 2:
+ startExtraSequence(kMars57GameLevel3, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 3:
+ _bombFuse.stopFuse();
+ _guessObject.disposeReactorGuess();
+ _undoPict.deallocateSurface();
+ _guessHistory.disposeReactorHistory();
+ _choiceHighlight.disposeReactorChoiceHighlight();
+ GameState.setScoringDisarmedCardBomb();
+ startExtraSequence(kMars57GameSolved, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ } else if (_guessHistory.getNumGuesses() >= 5) {
+ _vm->delayShell(2, 1);
+ bombExplodesInGame();
+ }
+ }
+}
+
+void Mars::bombExplodesInGame() {
+ _guessObject.disposeReactorGuess();
+ _undoPict.deallocateSurface();
+ _guessHistory.disposeReactorHistory();
+ _choiceHighlight.disposeReactorChoiceHighlight();
+ startExtraSequence(kMars57BombExplodesInGame, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void Mars::didntFindBomb() {
+ die(kDeathDidntFindMarsBomb);
+}
+
+Common::String Mars::getBriefingMovie() {
+ Common::String movieName = Neighborhood::getBriefingMovie();
+
+ if (!movieName.empty())
+ return movieName;
+
+ return "Images/AI/Mars/XM01";
+}
+
+Common::String Mars::getEnvScanMovie() {
+ Common::String movieName = Neighborhood::getEnvScanMovie();
+
+ if (movieName.empty()) {
+ RoomID room = GameState.getCurrentRoom();
+
+ if (room >= kMars0A && room <= kMars21)
+ return "Images/AI/Mars/XME1";
+ else if (room >= kMars22 && room <= kMars31South)
+ return "Images/AI/Mars/XME2";
+ else if (room >= kMars52 && room <= kMars58)
+ return "Images/AI/Mars/XMREACE";
+
+ return "Images/AI/Mars/XME3";
+ }
+
+ return movieName;
+}
+
+uint Mars::getNumHints() {
+ uint numHints = Neighborhood::getNumHints();
+
+ if (numHints == 0) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars27, kNorth):
+ case MakeRoomView(kMars28, kNorth):
+ case MakeRoomView(kMars49, kSouth):
+ numHints = 1;
+ break;
+ case MakeRoomView(kMars31, kSouth):
+ case MakeRoomView(kMars31South, kSouth):
+ if (!GameState.isTakenItemID(kMarsCard))
+ numHints = 1;
+ break;
+ case MakeRoomView(kMars34, kNorth):
+ if (!GameState.isTakenItemID(kMarsCard))
+ numHints = 2;
+ break;
+ case MakeRoomView(kMars34, kSouth):
+ case MakeRoomView(kMars45, kNorth):
+ if (!GameState.isTakenItemID(kCrowbar))
+ numHints = 1;
+ break;
+ case MakeRoomView(kMars51, kEast):
+ if (GameState.isCurrentDoorOpen() && !GameState.getShieldOn()) {
+ if (GameState.isTakenItemID(kShieldBiochip))
+ numHints = 1;
+ else
+ numHints = 2;
+ }
+ break;
+ case MakeRoomView(kMars52, kNorth):
+ case MakeRoomView(kMars52, kSouth):
+ case MakeRoomView(kMars52, kEast):
+ case MakeRoomView(kMars52, kWest):
+ case MakeRoomView(kMars54, kNorth):
+ case MakeRoomView(kMars54, kSouth):
+ case MakeRoomView(kMars54, kEast):
+ case MakeRoomView(kMars54, kWest):
+ case MakeRoomView(kMars56, kNorth):
+ case MakeRoomView(kMars56, kSouth):
+ case MakeRoomView(kMars56, kWest):
+ case MakeRoomView(kMars58, kNorth):
+ case MakeRoomView(kMars58, kSouth):
+ case MakeRoomView(kMars58, kEast):
+ case MakeRoomView(kMars58, kWest):
+ if (!GameState.getShieldOn()) {
+ if (GameState.isTakenItemID(kShieldBiochip))
+ numHints = 1;
+ else
+ numHints = 2;
+ }
+ break;
+ case MakeRoomView(kMars56, kEast):
+ if (getCurrentActivation() == kActivateReactorReadyForNitrogen) {
+ if ((ExtraID)_lastExtra == kMars57LowerScreenClosed)
+ numHints = 3;
+ } else if (getCurrentActivation() == kActivateReactorPlatformOut) {
+ if (!GameState.getShieldOn()) {
+ if (GameState.isTakenItemID(kShieldBiochip))
+ numHints = 1;
+ else
+ numHints = 2;
+ }
+ }
+ break;
+ }
+ }
+
+ return numHints;
+}
+
+Common::String Mars::getHintMovie(uint hintNum) {
+ Common::String movieName = Neighborhood::getHintMovie(hintNum);
+
+ if (movieName.empty()) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kMars27, kNorth):
+ case MakeRoomView(kMars28, kNorth):
+ return "Images/AI/Globals/XGLOB5C";
+ case MakeRoomView(kMars31, kSouth):
+ case MakeRoomView(kMars31South, kSouth):
+ case MakeRoomView(kMars34, kSouth):
+ case MakeRoomView(kMars45, kNorth):
+ return "Images/AI/Globals/XGLOB1C";
+ case MakeRoomView(kMars34, kNorth):
+ if (hintNum == 1)
+ return "Images/AI/Globals/XGLOB2C";
+
+ return "Images/AI/Globals/XGLOB3G";
+ case MakeRoomView(kMars49, kSouth):
+ if (GameState.isTakenItemID(kAirMask))
+ return "Images/AI/Globals/XGLOB3E";
+
+ return "Images/AI/Globals/XGLOB1C";
+ case MakeRoomView(kMars51, kEast):
+ if (GameState.isTakenItemID(kShieldBiochip))
+ return "Images/AI/Mars/XM52NW";
+
+ if (hintNum == 1)
+ return "Images/AI/Globals/XGLOB2D";
+
+ return "Images/AI/Globals/XGLOB3F";
+ case MakeRoomView(kMars52, kNorth):
+ case MakeRoomView(kMars52, kSouth):
+ case MakeRoomView(kMars52, kEast):
+ case MakeRoomView(kMars52, kWest):
+ case MakeRoomView(kMars54, kNorth):
+ case MakeRoomView(kMars54, kSouth):
+ case MakeRoomView(kMars54, kEast):
+ case MakeRoomView(kMars54, kWest):
+ case MakeRoomView(kMars56, kNorth):
+ case MakeRoomView(kMars56, kSouth):
+ case MakeRoomView(kMars56, kWest):
+ case MakeRoomView(kMars58, kNorth):
+ case MakeRoomView(kMars58, kSouth):
+ case MakeRoomView(kMars58, kEast):
+ case MakeRoomView(kMars58, kWest):
+ if (hintNum == 1) {
+ if (GameState.isTakenItemID(kShieldBiochip))
+ return "Images/AI/Mars/XM52NW";
+
+ return "Images/AI/Globals/XGLOB2D";
+ }
+
+ return "Images/AI/Globals/XGLOB3F";
+ case MakeRoomView(kMars56, kEast):
+ if (getCurrentActivation() == kActivateReactorReadyForNitrogen)
+ return Common::String::format("Images/AI/Mars/XM57SD%d", hintNum);
+
+ if (hintNum == 1) {
+ if (GameState.isTakenItemID(kShieldBiochip))
+ return "Images/AI/Mars/XM52NW";
+
+ return "Images/AI/Globals/XGLOB2D";
+ }
+
+ return "Images/AI/Globals/XGLOB3F";
+ }
+ }
+
+ return movieName;
+}
+
+bool Mars::inColorMatchingGame() {
+ return _guessObject.isDisplaying();
+}
+
+bool Mars::canSolve() {
+ return GameState.getCurrentRoomAndView() == MakeRoomView(kMars56, kEast) && (getCurrentActivation() == kActivateReactorReadyForNitrogen ||
+ getCurrentActivation() == kActivateReactorReadyForCrowBar || inColorMatchingGame());
+}
+
+void Mars::doSolve() {
+ if (getCurrentActivation() == kActivateReactorReadyForNitrogen || getCurrentActivation() == kActivateReactorReadyForCrowBar) {
+ _utilityFuse.stopFuse();
+ GameState.setMarsLockBroken(true);
+ GameState.setMarsLockFrozen(false);
+ startExtraLongSequence(kMars57OpenPanel, kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput);
+ } else if (inColorMatchingGame()) {
+ _bombFuse.stopFuse();
+ _guessObject.disposeReactorGuess();
+ _undoPict.deallocateSurface();
+ _guessHistory.disposeReactorHistory();
+ _choiceHighlight.disposeReactorChoiceHighlight();
+ startExtraSequence(kMars57GameSolved, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+Common::String Mars::getSoundSpotsName() {
+ return "Sounds/Mars/Mars Spots";
+}
+
+Common::String Mars::getNavMovieName() {
+ return "Images/Mars/Mars.movie";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/mars.h b/engines/pegasus/neighborhood/mars/mars.h
new file mode 100644
index 0000000000..0859522890
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/mars.h
@@ -0,0 +1,238 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_MARS_H
+#define PEGASUS_NEIGHBORHOOD_MARS_MARS_H
+
+#include "pegasus/neighborhood/neighborhood.h"
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/mars/energybeam.h"
+#include "pegasus/neighborhood/mars/gravitoncannon.h"
+#include "pegasus/neighborhood/mars/planetmover.h"
+#include "pegasus/neighborhood/mars/reactor.h"
+#include "pegasus/neighborhood/mars/robotship.h"
+#include "pegasus/neighborhood/mars/shuttleenergymeter.h"
+#include "pegasus/neighborhood/mars/shuttlehud.h"
+#include "pegasus/neighborhood/mars/spacejunk.h"
+#include "pegasus/neighborhood/mars/tractorbeam.h"
+
+namespace Pegasus {
+
+class InventoryItem;
+class Mars;
+
+enum MarsTimerCode {
+ kMarsLaunchTubeReached,
+ kMarsCanyonChaseFinished,
+ kMarsSpaceChaseFinished // Player ran out of time...
+};
+
+struct MarsTimerEvent {
+ Mars *mars;
+ MarsTimerCode event;
+
+ void fire();
+};
+
+enum ShuttleWeaponSelection {
+ kNoWeapon,
+ kEnergyBeam,
+ kGravitonCannon,
+ kTractorBeam
+};
+
+class Mars : public Neighborhood {
+friend struct MarsTimerEvent;
+public:
+ Mars(InputHandler *, PegasusEngine *);
+ virtual ~Mars();
+
+ void flushGameState();
+
+ virtual uint16 getDateResID() const;
+
+ virtual AirQuality getAirQuality(const RoomID);
+
+ void checkAirMask();
+
+ void showBigExplosion(const Common::Rect &, const DisplayOrder);
+ void showLittleExplosion(const Common::Rect &, const DisplayOrder);
+ void hitByJunk();
+ void decreaseRobotShuttleEnergy(const int, Common::Point impactPoint);
+ void setUpNextDropTime();
+
+ Common::String getBriefingMovie();
+ Common::String getEnvScanMovie();
+ uint getNumHints();
+ Common::String getHintMovie(uint);
+
+ virtual void shieldOn();
+ virtual void shieldOff();
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+ void setSoundFXLevel(const uint16);
+
+ bool canSolve();
+ void doSolve();
+
+ bool inColorMatchingGame();
+
+protected:
+ enum {
+ kMarsPrivatePodStorageOpenFlag,
+ kMarsPrivatePodTurnLeftFlag,
+ kMarsPrivatePodTurnRightFlag,
+ kMarsPrivateRobotTiredOfWaitingFlag,
+ kMarsPrivatePlatformZoomedInFlag,
+ kMarsPrivateBombExposedFlag,
+ kMarsPrivateDraggingBombFlag,
+ kMarsPrivateInSpaceChaseFlag,
+ kMarsPrivateGotMapChipFlag,
+ kMarsPrivateGotOpticalChipFlag,
+ kMarsPrivateGotShieldChipFlag,
+ kNumMarsPrivateFlags
+ };
+
+ void init();
+ void start();
+ void setUpAIRules();
+ void arriveAt(const RoomID, const DirectionConstant);
+ void takeItemFromRoom(Item *);
+ void dropItemIntoRoom(Item *, Hotspot *);
+ void activateHotspots();
+ void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *);
+ void clickInHotspot(const Input &, const Hotspot *);
+ InputBits getInputFilter();
+
+ TimeValue getViewTime(const RoomID, const DirectionConstant);
+ void getZoomEntry(const HotSpotID, ZoomTable::Entry &);
+ void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &);
+ CanOpenDoorReason canOpenDoor(DoorTable::Entry &);
+ void openDoor();
+ void closeDoorOffScreen(const RoomID, const DirectionConstant);
+ int16 getStaticCompassAngle(const RoomID, const DirectionConstant);
+ void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &);
+ void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &);
+ void turnTo(const DirectionConstant);
+ void receiveNotification(Notification *, const NotificationFlags);
+ void doorOpened();
+ void setUpReactorEnergyDrain();
+ Hotspot *getItemScreenSpot(Item *, DisplayElement *);
+ void lockThawed();
+ void robotTiredOfWaiting();
+
+ void setUpReactorLevel1();
+ void setUpNextReactorLevel();
+ void makeColorSequence();
+ void doUndoOneGuess();
+ void doReactorGuess(int32 guess);
+ void bombExplodesInGame();
+ void didntFindBomb();
+ CanMoveForwardReason canMoveForward(ExitTable::Entry &);
+ void cantMoveThatWay(CanMoveForwardReason);
+ void moveForward();
+ void bumpIntoWall();
+ void turnLeft();
+ void turnRight();
+ void airStageExpired();
+ void loadAmbientLoops();
+ void checkAirlockDoors();
+ void pickedUpItem(Item *item);
+ void cantOpenDoor(CanOpenDoorReason);
+ void launchMaze007Robot();
+ void launchMaze015Robot();
+ void launchMaze101Robot();
+ void launchMaze104Robot();
+ void launchMaze133Robot();
+ void launchMaze136Robot();
+ void launchMaze184Robot();
+ void timerExpired(const uint32);
+ void spotCompleted();
+
+ void doCanyonChase(void);
+ void startMarsTimer(TimeValue, TimeScale, MarsTimerCode);
+ void marsTimerExpired(MarsTimerEvent &);
+ void throwAwayMarsShuttle();
+ void startUpFromFinishedSpaceChase();
+ void startUpFromSpaceChase();
+ void transportToRobotShip();
+ void spaceChaseClick(const Input &, const HotSpotID);
+ void updateCursor(const Common::Point, const Hotspot *);
+
+ Common::String getSoundSpotsName();
+ Common::String getNavMovieName();
+
+ InventoryItem *_attackingItem;
+ FuseFunction _bombFuse;
+ FuseFunction _noAirFuse;
+ FuseFunction _utilityFuse;
+ FlagsArray<byte, kNumMarsPrivateFlags> _privateFlags;
+ uint _reactorStage, _nextGuess;
+ int32 _currentGuess[3];
+ ReactorGuess _guessObject;
+ Picture _undoPict;
+ ReactorHistory _guessHistory;
+ ReactorChoiceHighlight _choiceHighlight;
+
+ Picture _shuttleInterface1;
+ Picture _shuttleInterface2;
+ Picture _shuttleInterface3;
+ Picture _shuttleInterface4;
+ Movie _canyonChaseMovie;
+
+ MarsTimerEvent _marsEvent;
+
+ Movie _leftShuttleMovie;
+ Movie _rightShuttleMovie;
+ Movie _lowerLeftShuttleMovie;
+ Movie _lowerRightShuttleMovie;
+ Movie _centerShuttleMovie;
+ Movie _upperLeftShuttleMovie;
+ Movie _upperRightShuttleMovie;
+ Movie _leftDamageShuttleMovie;
+ Movie _rightDamageShuttleMovie;
+ ShuttleEnergyMeter _shuttleEnergyMeter;
+ Movie _planetMovie;
+ PlanetMover _planetMover;
+ RobotShip _robotShip;
+ ShuttleHUD _shuttleHUD;
+ TractorBeam _tractorBeam;
+ SpaceJunk _junk;
+ EnergyBeam _energyBeam;
+ GravitonCannon _gravitonCannon;
+ Hotspot _energyChoiceSpot;
+ Hotspot _gravitonChoiceSpot;
+ Hotspot _tractorChoiceSpot;
+ Hotspot _shuttleViewSpot;
+ Hotspot _shuttleTransportSpot;
+ ShuttleWeaponSelection _weaponSelection;
+ ScalingMovie _explosions;
+ NotificationCallBack _explosionCallBack;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/planetmover.cpp b/engines/pegasus/neighborhood/mars/planetmover.cpp
new file mode 100644
index 0000000000..a340120c12
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/planetmover.cpp
@@ -0,0 +1,104 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/movie.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/mars/hermite.h"
+#include "pegasus/neighborhood/mars/planetmover.h"
+#include "pegasus/neighborhood/mars/shuttleenergymeter.h"
+
+namespace Pegasus {
+
+static const TimeScale kRovingScale = kTractorBeamScale;
+static const TimeValue kRovingTime = kTenSeconds * kRovingScale;
+static const TimeValue kRovingSlop = kTwoSeconds * kRovingScale;
+
+static const CoordType kMaxVelocity = 20;
+
+PlanetMover::PlanetMover() {
+ setScale(kRovingScale);
+ _dropping = false;
+ _planetMovie = 0;
+}
+
+void PlanetMover::startMoving(Movie *planetMovie) {
+ _planetMovie = planetMovie;
+ _p4 = kPlanetStartTop;
+ _r4 = ((PegasusEngine *)g_engine)->getRandomNumber(kMaxVelocity - 1);
+ if (_r4 + _p4 < kPlanetStopTop)
+ _r4 = kPlanetStopTop - _p4;
+ newDestination();
+}
+
+void PlanetMover::stopMoving() {
+ stop();
+}
+
+void PlanetMover::dropPlanetOutOfSight() {
+ stop();
+ CoordType currentLoc = hermite(_p1, _p4, _r1, _r4, _lastTime, _duration);
+ CoordType currentV = dHermite(_p1, _p4, _r1, _r4, _lastTime, _duration);
+ _p1 = currentLoc;
+ _r1 = currentV;
+ _p4 = kPlanetStartTop;
+ _r4 = 0;
+ _duration = kTractorBeamTime - kTractorBeamScale;
+ _dropping = true;
+ setSegment(0, _duration);
+ setTime(0);
+ start();
+}
+
+void PlanetMover::newDestination() {
+ _p1 = _p4;
+ _r1 = _r4;
+
+ _p4 = kPlanetStopTop + ((PegasusEngine *)g_engine)->getRandomNumber(kPlanetStartTop - kPlanetStopTop - 1);
+ _r4 = ((PegasusEngine *)g_engine)->getRandomNumber(kMaxVelocity - 1);
+
+ if (_r4 + _p4 < kPlanetStopTop)
+ _r4 = kPlanetStopTop - _p4;
+
+ stop();
+ _duration = kRovingTime + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingSlop - 1);
+ setSegment(0, _duration);
+ setTime(0);
+ start();
+}
+
+void PlanetMover::timeChanged(const TimeValue) {
+ if (_planetMovie) {
+ _planetMovie->moveElementTo(kPlanetStartLeft, hermite(_p1, _p4, _r1, _r4, _lastTime, _duration));
+ if (_lastTime == _duration) {
+ if (_dropping)
+ stop();
+ else
+ newDestination();
+ }
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/planetmover.h b/engines/pegasus/neighborhood/mars/planetmover.h
new file mode 100644
index 0000000000..2c195387e8
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/planetmover.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_PLANETMOVER_H
+#define PEGASUS_NEIGHBORHOOD_MARS_PLANETMOVER_H
+
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class Movie;
+
+class PlanetMover : IdlerTimeBase {
+public:
+ PlanetMover();
+ virtual ~PlanetMover() {}
+
+ void startMoving(Movie *);
+ void stopMoving();
+
+ void dropPlanetOutOfSight();
+
+protected:
+ void newDestination();
+ virtual void timeChanged(const TimeValue);
+
+ Movie *_planetMovie;
+ CoordType _p1, _p4, _r1, _r4;
+ TimeValue _duration;
+ bool _dropping;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/reactor.cpp b/engines/pegasus/neighborhood/mars/reactor.cpp
new file mode 100644
index 0000000000..334fb98879
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/reactor.cpp
@@ -0,0 +1,297 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * aint32 with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/mars/reactor.h"
+
+namespace Pegasus {
+
+static const CoordType kCurrentGuessWidth = 121;
+static const CoordType kCurrentGuessHeight = 23;
+
+static const CoordType kOneGuessWidth = 25;
+static const CoordType kOneGuessHeight = 23;
+
+static const ResIDType kReactorChoicesPICTID = 905;
+
+static const CoordType kCurrentGuessLeft = kNavAreaLeft + 146;
+static const CoordType kCurrentGuessTop = kNavAreaTop + 90;
+
+ReactorGuess::ReactorGuess(const DisplayElementID id) : DisplayElement(id) {
+ setBounds(kCurrentGuessLeft, kCurrentGuessTop, kCurrentGuessLeft + kCurrentGuessWidth,
+ kCurrentGuessTop + kCurrentGuessHeight);
+ setDisplayOrder(kMonitorLayer);
+ _currentGuess[0] = -1;
+ _currentGuess[1] = -1;
+ _currentGuess[2] = -1;
+}
+
+void ReactorGuess::initReactorGuess() {
+ _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorChoicesPICTID);
+ startDisplaying();
+ show();
+}
+
+void ReactorGuess::disposeReactorGuess() {
+ stopDisplaying();
+ _colors.deallocateSurface();
+}
+
+void ReactorGuess::setGuess(int32 a, int32 b, int32 c) {
+ _currentGuess[0] = a;
+ _currentGuess[1] = b;
+ _currentGuess[2] = c;
+ triggerRedraw();
+}
+
+void ReactorGuess::draw(const Common::Rect &) {
+ if (_colors.isSurfaceValid()) {
+ Common::Rect r1(0, 0, kOneGuessWidth, kOneGuessHeight);
+ Common::Rect r2 = r1;
+
+ for (int i = 0; i < 3; i++) {
+ if (_currentGuess[i] >= 0) {
+ r1.moveTo(kOneGuessWidth * _currentGuess[i], 0);
+ r2.moveTo(kCurrentGuessLeft + 48 * i, kCurrentGuessTop);
+ _colors.copyToCurrentPortTransparent(r1, r2);
+ }
+ }
+ }
+}
+
+static const CoordType kReactorChoiceHiliteWidth = 166;
+static const CoordType kReactorChoiceHiliteHeight = 26;
+
+static const CoordType kChoiceHiliteLefts[6] = {
+ 0,
+ 34,
+ 34 + 34,
+ 34 + 34 + 32,
+ 34 + 34 + 32 + 34,
+ 34 + 34 + 32 + 34 + 32
+};
+
+static const ResIDType kReactorChoiceHilitePICTID = 901;
+
+static const CoordType kReactorChoiceHiliteLeft = kNavAreaLeft + 116;
+static const CoordType kReactorChoiceHiliteTop = kNavAreaTop + 158;
+
+ReactorChoiceHighlight::ReactorChoiceHighlight(const DisplayElementID id) : DisplayElement(id) {
+ setBounds(kReactorChoiceHiliteLeft, kReactorChoiceHiliteTop, kReactorChoiceHiliteLeft + kReactorChoiceHiliteWidth,
+ kReactorChoiceHiliteTop + kReactorChoiceHiliteHeight);
+ setDisplayOrder(kMonitorLayer);
+}
+
+void ReactorChoiceHighlight::initReactorChoiceHighlight() {
+ _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorChoiceHilitePICTID);
+ startDisplaying();
+ show();
+}
+
+void ReactorChoiceHighlight::disposeReactorChoiceHighlight() {
+ stopDisplaying();
+ _colors.deallocateSurface();
+}
+
+void ReactorChoiceHighlight::draw(const Common::Rect &) {
+ if (_colors.isSurfaceValid()) {
+ for (int i = 0; i < 5; ++i) {
+ if (_choices.getFlag(i)) {
+ Common::Rect r1(0, 0, kChoiceHiliteLefts[i + 1] - kChoiceHiliteLefts[i], kReactorChoiceHiliteHeight);
+ Common::Rect r2 = r1;
+ r1.moveTo(kChoiceHiliteLefts[i], 0);
+ r2.moveTo(kReactorChoiceHiliteLeft + kChoiceHiliteLefts[i], kReactorChoiceHiliteTop);
+ _colors.copyToCurrentPort(r1, r2);
+ }
+ }
+ }
+}
+
+static const CoordType kReactorHistoryWidth = 128;
+static const CoordType kReactorHistoryHeight = 168;
+
+static const CoordType kColorWidths[5] = { 24, 25, 25, 26, 27 };
+static const CoordType kColorHeights[5] = { 14, 15, 17, 17, 19};
+
+static const CoordType kHistoryLefts[5][3] = {
+ { 302 + kNavAreaLeft, 329 + kNavAreaLeft, 357 + kNavAreaLeft },
+ { 302 + kNavAreaLeft, 331 + kNavAreaLeft, 360 + kNavAreaLeft },
+ { 303 + kNavAreaLeft, 333 + kNavAreaLeft, 363 + kNavAreaLeft },
+ { 304 + kNavAreaLeft, 335 + kNavAreaLeft, 366 + kNavAreaLeft },
+ { 305 + kNavAreaLeft, 337 + kNavAreaLeft, 369 + kNavAreaLeft }
+};
+
+static const CoordType kHistoryTops[5] = {
+ 39 + kNavAreaTop,
+ 61 + kNavAreaTop,
+ 84 + kNavAreaTop,
+ 110 + kNavAreaTop,
+ 137 + kNavAreaTop
+};
+
+static const CoordType kOneAnswerWidth = 35;
+static const CoordType kOneAnswerHeight = 27;
+
+static const CoordType kDigitWidth = 16;
+static const CoordType kDigitHeight = 12;
+
+static const CoordType kCorrectCountLefts[5] = {
+ 388 + kNavAreaLeft,
+ 392 + kNavAreaLeft,
+ 398 + kNavAreaLeft,
+ 402 + kNavAreaLeft,
+ 406 + kNavAreaLeft
+};
+
+static const CoordType kCorrectCountTops[5] = {
+ 40 + kNavAreaTop,
+ 62 + kNavAreaTop,
+ 86 + kNavAreaTop,
+ 112 + kNavAreaTop,
+ 140 + kNavAreaTop
+};
+
+static const ResIDType kReactorDigitsPICTID = 902;
+static const ResIDType kReactorHistoryPICTID = 903;
+static const ResIDType kReactorAnswerPICTID = 904;
+
+static const CoordType kReactorHistoryLeft = kNavAreaLeft + 302;
+static const CoordType kReactorHistoryTop = kNavAreaTop + 39;
+
+static const CoordType kAnswerLeft = kNavAreaLeft + 304;
+static const CoordType kAnswerTop = kNavAreaTop + 180;
+
+ReactorHistory::ReactorHistory(const DisplayElementID id) : DisplayElement(id) {
+ setBounds(kReactorHistoryLeft, kReactorHistoryTop, kReactorHistoryLeft + kReactorHistoryWidth,
+ kReactorHistoryTop + kReactorHistoryHeight);
+ setDisplayOrder(kMonitorLayer);
+ _numGuesses = 0;
+ _answer[0] = -1;
+ _answer[1] = -1;
+ _answer[2] = -1;
+ _showAnswer = false;
+}
+
+void ReactorHistory::initReactorHistory() {
+ _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorHistoryPICTID);
+ _digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorDigitsPICTID);
+ _answerColors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorAnswerPICTID);
+ startDisplaying();
+ show();
+}
+
+void ReactorHistory::disposeReactorHistory() {
+ stopDisplaying();
+ _colors.deallocateSurface();
+}
+
+void ReactorHistory::addGuess(int32 a, int32 b, int32 c) {
+ _history[_numGuesses][0] = a;
+ _history[_numGuesses][1] = b;
+ _history[_numGuesses][2] = c;
+ _numGuesses++;
+ triggerRedraw();
+}
+
+void ReactorHistory::clearHistory() {
+ _numGuesses = 0;
+ _showAnswer = false;
+ triggerRedraw();
+}
+
+void ReactorHistory::setAnswer(int32 a, int32 b, int32 c) {
+ _answer[0] = a;
+ _answer[1] = b;
+ _answer[2] = c;
+}
+
+void ReactorHistory::showAnswer() {
+ _showAnswer = true;
+ triggerRedraw();
+}
+
+bool ReactorHistory::isSolved() {
+ for (int i = 0; i < _numGuesses; i++)
+ if (_history[i][0] == _answer[0] && _history[i][1] == _answer[1] && _history[i][2] == _answer[2])
+ return true;
+
+ return false;
+}
+
+void ReactorHistory::draw(const Common::Rect &) {
+ static const CoordType kColorTops[5] = {
+ 0,
+ kColorHeights[0],
+ kColorHeights[0] + kColorHeights[1],
+ kColorHeights[0] + kColorHeights[1] + kColorHeights[2],
+ kColorHeights[0] + kColorHeights[1] + kColorHeights[2] + kColorHeights[3],
+ };
+
+ if (_colors.isSurfaceValid() && _digits.isSurfaceValid()) {
+ for (int i = 0; i < _numGuesses; ++i) {
+ Common::Rect r1(0, 0, kColorWidths[i], kColorHeights[i]);
+ Common::Rect r2 = r1;
+ Common::Rect r3(0, 0, kDigitWidth, kDigitHeight);
+ Common::Rect r4 = r3;
+ int correct = 0;
+
+ for (int j = 0; j < 3; ++j) {
+ r1.moveTo(kColorWidths[i] * _history[i][j], kColorTops[i]);
+ r2.moveTo(kHistoryLefts[i][j], kHistoryTops[i]);
+ _colors.copyToCurrentPortTransparent(r1, r2);
+
+ if (_history[i][j] == _answer[j])
+ correct++;
+ }
+
+ r3.moveTo(kDigitWidth * correct, 0);
+ r4.moveTo(kCorrectCountLefts[i], kCorrectCountTops[i]);
+ _digits.copyToCurrentPort(r3, r4);
+ }
+
+ if (_showAnswer && _answerColors.isSurfaceValid()) {
+ Common::Rect r1(0, 0, kOneAnswerWidth, kOneAnswerHeight);
+ Common::Rect r2 = r1;
+
+ for (int i = 0; i < 3; i++) {
+ r1.moveTo(kOneAnswerWidth * _answer[i], 0);
+ r2.moveTo(kAnswerLeft + 34 * i, kAnswerTop);
+ _answerColors.copyToCurrentPortTransparent(r1, r2);
+ }
+ }
+ }
+}
+
+int32 ReactorHistory::getCurrentNumCorrect() {
+ int correct = 0;
+
+ for (int i = 0; i < 3; i++)
+ if (_history[_numGuesses - 1][i] == _answer[i])
+ correct++;
+
+ return correct;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/reactor.h b/engines/pegasus/neighborhood/mars/reactor.h
new file mode 100644
index 0000000000..86338f8266
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/reactor.h
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_REACTOR_H
+#define PEGASUS_NEIGHBORHOOD_MARS_REACTOR_H
+
+#include "pegasus/elements.h"
+#include "pegasus/surface.h"
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+class ReactorGuess : public DisplayElement {
+public:
+ ReactorGuess(const DisplayElementID);
+ virtual ~ReactorGuess() {}
+
+ void initReactorGuess();
+ void disposeReactorGuess();
+
+ void setGuess(int32, int32, int32);
+
+ void draw(const Common::Rect &);
+
+protected:
+ int32 _currentGuess[3];
+
+ Surface _colors;
+};
+
+class ReactorChoiceHighlight : public DisplayElement {
+public:
+ ReactorChoiceHighlight(const DisplayElementID);
+ virtual ~ReactorChoiceHighlight() {}
+
+ void initReactorChoiceHighlight();
+ void disposeReactorChoiceHighlight();
+
+ void resetHighlight() {
+ _choices.clearAllFlags();
+ triggerRedraw();
+ }
+
+ bool choiceHighlighted(uint32 whichChoice) { return _choices.getFlag(whichChoice); }
+
+ void draw(const Common::Rect &);
+
+ void highlightChoice(uint32 whichChoice) {
+ _choices.setFlag(whichChoice);
+ triggerRedraw();
+ }
+
+protected:
+ Surface _colors;
+ FlagsArray<byte, 5> _choices;
+};
+
+class ReactorHistory : public DisplayElement {
+public:
+ ReactorHistory(const DisplayElementID);
+ virtual ~ReactorHistory() {}
+
+ void initReactorHistory();
+ void disposeReactorHistory();
+
+ void draw(const Common::Rect &);
+
+ void addGuess(int32, int32, int32);
+ int32 getNumGuesses() { return _numGuesses; }
+ void clearHistory();
+ void setAnswer(int32, int32, int32);
+ void showAnswer();
+ bool isSolved();
+ int32 getCurrentNumCorrect();
+
+protected:
+ Surface _colors, _digits, _answerColors;
+ int32 _answer[3];
+ int32 _history[5][3];
+ int32 _numGuesses;
+ bool _showAnswer;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/robotship.cpp b/engines/pegasus/neighborhood/mars/robotship.cpp
new file mode 100644
index 0000000000..1f4bbc1779
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/robotship.cpp
@@ -0,0 +1,267 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/mars/hermite.h"
+#include "pegasus/neighborhood/mars/mars.h"
+#include "pegasus/neighborhood/mars/robotship.h"
+#include "pegasus/neighborhood/mars/spacechase3d.h"
+#include "pegasus/neighborhood/mars/spacejunk.h"
+
+namespace Pegasus {
+
+static const TimeScale kRovingScale = kTractorBeamScale;
+static const TimeValue kRovingTime = kSixSeconds * kRovingScale;
+static const TimeValue kRovingSlop = kThreeSeconds * kRovingScale;
+
+static const int kNumSpriteColumns = 15;
+static const int kNumSpriteRows = 16;
+
+static const CoordType kInitialLocationLeft = kShuttleWindowLeft - 50;
+static const CoordType kInitialLocationTop = kShuttleWindowTop - 50;
+static const CoordType kInitialLocationWidth = kShuttleWindowWidth + 100;
+static const CoordType kInitialLocationHeight = kShuttleWindowHeight + 100;
+
+static const CoordType kVelocityVectorLength = 100;
+static const CoordType kVelocityVectorSlop = 50;
+
+static const CoordType kRovingLeft = kShuttleWindowLeft + 20;
+static const CoordType kRovingTop = kShuttleWindowTop + 20;
+static const CoordType kRovingWidth = kShuttleWindowMidH - kRovingLeft;
+static const CoordType kRovingHeight = kShuttleWindowMidV - kRovingTop;
+
+RobotShip *g_robotShip = 0;
+
+RobotShip::RobotShip() : _spritesMovie(kNoDisplayElement) {
+ g_robotShip = this;
+ _shipRange = Common::Rect(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth,
+ kShuttleWindowTop + kShuttleWindowHeight);
+ setScale(kRovingScale);
+ _currentLocation.x = 0;
+ _currentLocation.y = 0;
+ _snaring = false;
+ _dropJunkFuse.setFunctor(new Common::Functor0Mem<void, RobotShip>(this, &RobotShip::timeToDropJunk));
+ _duration = 0xFFFFFFFF;
+}
+
+RobotShip::~RobotShip() {
+ g_robotShip = 0;
+}
+
+void RobotShip::initRobotShip() {
+ _spritesMovie.initFromMovieFile("Images/Mars/Ship.movie", true);
+ _spritesMovie.setDisplayOrder(kShuttleRobotShipOrder);
+ _spritesMovie.moveElementTo(kPlanetStartLeft, kPlanetStartTop);
+ _spritesMovie.startDisplaying();
+ _spritesMovie.show();
+
+ Common::Rect r;
+ _spritesMovie.getBounds(r);
+ _shipWidth = r.width();
+ _shipHeight = r.height();
+ _dead = false;
+}
+
+void RobotShip::cleanUpRobotShip() {
+ _dropJunkFuse.stopFuse();
+ _spritesMovie.stopDisplaying();
+ _spritesMovie.releaseMovie();
+}
+
+void RobotShip::startMoving() {
+ if (((PegasusEngine *)g_engine)->getRandomBit()) {
+ _p4.x = kInitialLocationLeft + ((PegasusEngine *)g_engine)->getRandomNumber(kInitialLocationWidth - 1);
+ if (((PegasusEngine *)g_engine)->getRandomBit())
+ _p4.y = kInitialLocationTop;
+ else
+ _p4.y = kInitialLocationTop + kInitialLocationHeight;
+ } else {
+ _p4.y = kInitialLocationTop + ((PegasusEngine *)g_engine)->getRandomNumber(kInitialLocationHeight - 1);
+ if (((PegasusEngine *)g_engine)->getRandomBit())
+ _p4.x = kInitialLocationLeft;
+ else
+ _p4.x = kInitialLocationLeft + kInitialLocationWidth;
+ }
+
+ makeVelocityVector(_p4.x, _p4.y, kShuttleWindowLeft + kShuttleWindowWidth / 2,
+ kShuttleWindowTop + kShuttleWindowHeight / 2, _r4);
+ newDestination();
+ setUpNextDropTime();
+}
+
+void RobotShip::killRobotShip() {
+ cleanUpRobotShip();
+ _dead = true;
+}
+
+void RobotShip::setUpNextDropTime() {
+ if (!isSnared()) {
+ _dropJunkFuse.primeFuse(kJunkDropBaseTime + ((PegasusEngine *)g_engine)->getRandomNumber(kJunkDropSlopTime));
+ _dropJunkFuse.lightFuse();
+ }
+}
+
+void RobotShip::timeToDropJunk() {
+ if (g_spaceJunk) {
+ CoordType x, y;
+ _spritesMovie.getCenter(x, y);
+ g_spaceJunk->launchJunk(((PegasusEngine *)g_engine)->getRandomNumber(24), x, y);
+ }
+}
+
+void RobotShip::newDestination() {
+ _p1 = _p4;
+ _r1 = _r4;
+
+ _p4.x = kRovingLeft + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingWidth - 1);
+ _p4.y = kRovingTop + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingHeight - 1);
+
+ if (((PegasusEngine *)g_engine)->getRandomNumber(7) < 6) {
+ if (!sameSign(_p4.x - kShuttleWindowMidH, kShuttleWindowMidH - _p1.x)) {
+ if (sign(_p4.x - kShuttleWindowMidH) > 0)
+ _p4.x -= kRovingWidth;
+ else
+ _p4.x += kRovingWidth;
+ }
+ }
+
+ if (((PegasusEngine *)g_engine)->getRandomNumber(7) < 6) {
+ if (!sameSign(_p4.y - kShuttleWindowMidV, kShuttleWindowMidV - _p1.y)) {
+ if (sign(_p4.y - kShuttleWindowMidV) > 0)
+ _p4.y -= kRovingHeight;
+ else
+ _p4.y += kRovingHeight;
+ }
+ }
+
+ makeVelocityVector(_p4.x, _p4.y, kShuttleWindowLeft + kShuttleWindowWidth / 2,
+ kShuttleWindowTop + kShuttleWindowHeight / 2, _r4);
+ stop();
+ _duration = kRovingTime + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingSlop - 1);
+ setSegment(0, _duration);
+ setTime(0);
+ start();
+}
+
+void RobotShip::moveRobotTo(CoordType x, CoordType y) {
+ _currentLocation.x = x;
+ _currentLocation.y = y;
+
+ if (_spritesMovie.isMovieValid()) {
+ _spritesMovie.moveElementTo(x - (_shipWidth >> 1), y - (_shipHeight >> 1));
+
+ if (x < _shipRange.left)
+ x = 0;
+ else if (x > _shipRange.right - 1)
+ x = _shipRange.width() - 1;
+ else
+ x -= _shipRange.left;
+
+ if (y < _shipRange.top)
+ y = 0;
+ else if (y > _shipRange.bottom - 1)
+ y = _shipRange.height() - 1;
+ else
+ y -= _shipRange.top;
+
+ x = kNumSpriteColumns * x / _shipRange.width();
+ y = kNumSpriteRows * y / _shipRange.height();
+
+ _spritesMovie.setTime(40 * (x + y * kNumSpriteColumns));
+ _spritesMovie.redrawMovieWorld();
+ }
+}
+
+bool RobotShip::pointInShuttle(Common::Point &pt) {
+ Common::Rect r;
+ _spritesMovie.getBounds(r);
+
+ int dx = r.width() / 4;
+ int dy = r.height() / 6;
+
+ r.left += dx;
+ r.right -= dx;
+ r.top += dy;
+ r.bottom -= dy;
+
+ return r.contains(pt);
+}
+
+void RobotShip::hitByEnergyBeam(Common::Point impactPoint) {
+ ((Mars *)g_neighborhood)->decreaseRobotShuttleEnergy(1, impactPoint);
+ setGlowing(true);
+ ((PegasusEngine *)g_engine)->delayShell(1, 3);
+ setGlowing(false);
+}
+
+void RobotShip::hitByGravitonCannon(Common::Point impactPoint) {
+ GameState.setMarsHitRobotWithCannon(true);
+ ((Mars *)g_neighborhood)->decreaseRobotShuttleEnergy(6, impactPoint);
+}
+
+void RobotShip::snareByTractorBeam() {
+ _dropJunkFuse.stopFuse();
+ stop();
+
+ Common::Point currentV;
+ dHermite(_p1, _p4, _r1, _r4, _lastTime, _duration, currentV);
+
+ _p1 = _currentLocation;
+ _r1 = currentV;
+ _p4.x = kShuttleWindowMidH;
+ _p4.y = kShuttleWindowMidV;
+ _r4.x = 0;
+ _r4.y = 0;
+ _duration = kTractorBeamTime;
+ _snaring = true;
+ setSegment(0, _duration);
+ setTime(0);
+ start();
+}
+
+void RobotShip::timeChanged(const TimeValue) {
+ Common::Point newLocation;
+ hermite(_p1, _p4, _r1, _r4, _lastTime, _duration, newLocation);
+ moveRobotTo(newLocation.x, newLocation.y);
+
+ if (_lastTime == _duration) {
+ if (_snaring)
+ stop();
+ else
+ newDestination();
+ }
+}
+
+void RobotShip::makeVelocityVector(CoordType x1, CoordType y1, CoordType x2, CoordType y2, Common::Point &vector) {
+ CoordType length = ((PegasusEngine *)g_engine)->getRandomNumber(kVelocityVectorSlop - 1) + kVelocityVectorLength;
+ vector.x = x2 - x1;
+ vector.y = y2 - y1;
+ float oldLength = sqrt((float)(vector.x * vector.x + vector.y * vector.y));
+ vector.x = (int)(vector.x * length / oldLength);
+ vector.y = (int)(vector.y * length / oldLength);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/robotship.h b/engines/pegasus/neighborhood/mars/robotship.h
new file mode 100644
index 0000000000..04be3ea56e
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/robotship.h
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_ROBOTSHIP_H
+#define PEGASUS_NEIGHBORHOOD_MARS_ROBOTSHIP_H
+
+#include "pegasus/movie.h"
+
+namespace Pegasus {
+
+static const CoordType kShuttleMovieWidth = 114;
+static const CoordType kShuttleMovieHeight = 42;
+
+class RobotShip : IdlerTimeBase {
+public:
+ RobotShip();
+ virtual ~RobotShip();
+
+ void initRobotShip();
+ void cleanUpRobotShip();
+
+ void startMoving();
+
+ void killRobotShip();
+
+ bool pointInShuttle(Common::Point&);
+
+ void hitByEnergyBeam(Common::Point impactPoint);
+ void hitByGravitonCannon(Common::Point impactPoint);
+
+ void getShuttleBounds(Common::Rect &r) { _spritesMovie.getBounds(r); }
+
+ void setGlowing(const bool glowing) { _spritesMovie.setGlowing(glowing); }
+
+ void snareByTractorBeam();
+ bool isSnared() { return _snaring && getTime() == _duration; }
+
+ bool isDead() { return _dead; }
+
+ void setUpNextDropTime();
+
+protected:
+ void newDestination();
+ void moveRobotTo(CoordType, CoordType);
+ void timeToDropJunk();
+ virtual void timeChanged(const TimeValue);
+ void makeVelocityVector(CoordType, CoordType, CoordType, CoordType, Common::Point &);
+
+ GlowingMovie _spritesMovie;
+ Common::Rect _shipRange;
+ int _shipWidth, _shipHeight;
+ Common::Point _p1, _p4, _r1, _r4, _currentLocation;
+ FuseFunction _dropJunkFuse;
+ TimeValue _duration;
+ bool _snaring, _dead;
+};
+
+extern RobotShip *g_robotShip;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp b/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp
new file mode 100644
index 0000000000..cd08dbae6a
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp
@@ -0,0 +1,116 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/mars/shuttleenergymeter.h"
+
+namespace Pegasus {
+
+ShuttleEnergyMeter::ShuttleEnergyMeter() : FaderAnimation(kNoDisplayElement) {
+ setBounds(kShuttleEnergyLeft, kShuttleEnergyTop, kShuttleEnergyLeft + kShuttleEnergyWidth,
+ kShuttleEnergyTop + kShuttleEnergyHeight);
+ setDisplayOrder(kShuttleStatusOrder);
+ setFaderValue(0);
+}
+
+void ShuttleEnergyMeter::initShuttleEnergyMeter() {
+ _meterImage.getImageFromPICTFile("Images/Mars/Shuttle Energy.pict");
+ _lowWarning.getImageFromPICTFile("Images/Mars/Shuttle Low Energy.pict");
+ startDisplaying();
+ show();
+}
+
+void ShuttleEnergyMeter::disposeShuttleEnergyMeter() {
+ stopFader();
+ hide();
+ stopDisplaying();
+ _meterImage.deallocateSurface();
+ _lowWarning.deallocateSurface();
+}
+
+void ShuttleEnergyMeter::draw(const Common::Rect &) {
+ int32 currentValue = getFaderValue();
+
+ Common::Rect r1, r2, bounds;
+ getBounds(bounds);
+
+ if (currentValue < kLowShuttleEnergy) {
+ _lowWarning.getSurfaceBounds(r1);
+ r2 = r1;
+ r2.moveTo(bounds.left, bounds.top);
+ _lowWarning.copyToCurrentPort(r1, r2);
+ }
+
+ _meterImage.getSurfaceBounds(r1);
+ r1.right = r1.left + r1.width() * currentValue / kFullShuttleEnergy;
+ r2 = r1;
+ r2.moveTo(bounds.left + 102, bounds.top + 6);
+ _meterImage.copyToCurrentPort(r1, r2);
+}
+
+void ShuttleEnergyMeter::powerUpMeter() {
+ FaderMoveSpec moveSpec;
+ moveSpec.makeTwoKnotFaderSpec(kThirtyTicksPerSecond, 0, 0, 45, kFullShuttleEnergy);
+ startFader(moveSpec);
+}
+
+void ShuttleEnergyMeter::setEnergyValue(const int32 value) {
+ stopFader();
+ FaderMoveSpec moveSpec;
+ moveSpec.makeTwoKnotFaderSpec(kFifteenTicksPerSecond, value * 3, value, kFullShuttleEnergy * 3, kFullShuttleEnergy);
+ startFader(moveSpec);
+}
+
+void ShuttleEnergyMeter::drainForTractorBeam() {
+ stopFader();
+ TimeValue startTime = 0, stopTime;
+ int32 startValue = getFaderValue(), stopValue;
+
+ if (startValue < kTractorBeamEnergy) {
+ stopTime = startValue * kTractorBeamTime / kTractorBeamEnergy;
+ stopValue = 0;
+ } else {
+ stopTime = kTractorBeamTime;
+ stopValue = startValue - kTractorBeamEnergy;
+ }
+
+ FaderMoveSpec moveSpec;
+ moveSpec.makeTwoKnotFaderSpec(kTractorBeamScale, startTime, startValue, stopTime, stopValue);
+ startFader(moveSpec);
+}
+
+int32 ShuttleEnergyMeter::getEnergyValue() const {
+ return getFaderValue();
+}
+
+void ShuttleEnergyMeter::dropEnergyValue(const int32 delta) {
+ setEnergyValue(getFaderValue() - delta);
+}
+
+bool ShuttleEnergyMeter::enoughEnergyForTractorBeam() const {
+ return getEnergyValue() >= kTractorBeamEnergy;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/shuttleenergymeter.h b/engines/pegasus/neighborhood/mars/shuttleenergymeter.h
new file mode 100644
index 0000000000..51161e094e
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/shuttleenergymeter.h
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEENERGYMETER_H
+#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEENERGYMETER_H
+
+#include "pegasus/fader.h"
+#include "pegasus/surface.h"
+
+namespace Pegasus {
+
+static const int32 kFullShuttleEnergy = 100;
+// Low is 20 percent
+static const int32 kLowShuttleEnergy = kFullShuttleEnergy * 20 / 100;
+
+static const int32 kMinDampingEnergy = 15;
+static const int32 kMinGravitonEnergy = 63;
+
+static const TimeScale kTractorBeamScale = kFifteenTicksPerSecond;
+static const TimeValue kTractorBeamTime = kFiveSeconds * kTractorBeamScale;
+static const int32 kTractorBeamEnergy = kLowShuttleEnergy;
+
+class ShuttleEnergyMeter : public FaderAnimation {
+public:
+ ShuttleEnergyMeter();
+ ~ShuttleEnergyMeter() {}
+
+ void initShuttleEnergyMeter();
+ void disposeShuttleEnergyMeter();
+
+ void powerUpMeter();
+
+ void setEnergyValue(const int32);
+ int32 getEnergyValue() const;
+
+ void dropEnergyValue(const int32);
+
+ void drainForTractorBeam();
+
+ bool enoughEnergyForTractorBeam() const;
+
+ void draw(const Common::Rect &);
+
+protected:
+ Surface _meterImage;
+ Surface _lowWarning;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.cpp b/engines/pegasus/neighborhood/mars/shuttlehud.cpp
new file mode 100644
index 0000000000..11e826278b
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/shuttlehud.cpp
@@ -0,0 +1,246 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/mars/robotship.h"
+#include "pegasus/neighborhood/mars/shuttlehud.h"
+
+namespace Pegasus {
+
+static const CoordType kHUDTargetGridLeft = kShuttleWindowLeft + 16;
+static const CoordType kHUDTargetGridTop = kShuttleWindowTop + 8;
+static const CoordType kHUDTargetGridWidth = 328;
+static const CoordType kHUDTargetGridHeight = 206;
+
+static const CoordType kHUDRS232Left = kHUDTargetGridLeft + 264;
+static const CoordType kHUDRS232Top = kHUDTargetGridTop + 2;
+
+static const CoordType kHUDLockLeft = kShuttleWindowLeft + 101;
+static const CoordType kHUDLockTop = kShuttleWindowTop + 49;
+static const CoordType kHUDLockWidth = 145;
+static const CoordType kHUDLockHeight = 124;
+
+static const CoordType kTractorLockWidth = 50;
+static const CoordType kTractorLockHeight = 30;
+
+static const CoordType kTractorLockLeft = kShuttleWindowMidH - kTractorLockWidth / 2;
+static const CoordType kTractorLockTop = kShuttleWindowMidV - kTractorLockHeight / 2;
+static const CoordType kTractorLockRight = kTractorLockLeft + kTractorLockWidth;
+static const CoordType kTractorLockBottom = kTractorLockTop + kTractorLockHeight;
+
+static const uint16 s_RS232Data[] = {
+ 0xF0E1, 0xCE70,
+ 0xF9E1, 0xEF78,
+ 0x4900, 0x2108,
+ 0x79C0, 0xE738,
+ 0x70E1, 0xC770,
+ 0x5821, 0x0140,
+ 0x4DE1, 0xEF78,
+ 0x45C1, 0xEE78
+};
+
+static const uint16 s_lockData[] = {
+ 0xE007, 0xFE1F, 0xF8E0, 0x7000,
+ 0xE00F, 0xFF3F, 0xFCE0, 0xE000,
+ 0xE00E, 0x0738, 0x1CE1, 0xC000,
+ 0xE00E, 0x0738, 0x00FF, 0x8000,
+ 0xE00E, 0x0738, 0x00FF, 0x0000,
+ 0xE00E, 0x0738, 0x00E3, 0x8000,
+ 0xE00E, 0x0738, 0x1CE1, 0xC000,
+ 0xFFCF, 0xFF3F, 0xFCE0, 0xE000,
+ 0xFFC7, 0xFE1F, 0xF8E0, 0x7000
+};
+
+#define drawHUDLockLine(x1, y1, x2, y2, penX, penY, color) \
+ screen->drawThickLine((x1) + kHUDLockLeft, (y1) + kHUDLockTop, \
+ (x2) + kHUDLockLeft, (y2) + kHUDLockTop, penX, penY, color)
+
+#define drawHUDLockArrows(offset, color) \
+ drawHUDLockLine(63, 0 + (offset), 68, 5 + (offset), 1, 3, color); \
+ drawHUDLockLine(71, 8 + (offset), 77, 14 + (offset), 1, 3, color); \
+ drawHUDLockLine(78, 14 + (offset), 84, 8 + (offset), 1, 3, color); \
+ drawHUDLockLine(87, 5 + (offset), 92, 0 + (offset), 1, 3, color); \
+ drawHUDLockLine(63, 121 - (offset), 68, 116 - (offset), 1, 3, color); \
+ drawHUDLockLine(71, 113 - (offset), 77, 107 - (offset), 1, 3, color); \
+ drawHUDLockLine(78, 107 - (offset), 84, 113 - (offset), 1, 3, color); \
+ drawHUDLockLine(87, 116 - (offset), 92, 121 - (offset), 1, 3, color); \
+\
+ drawHUDLockLine(13 + (offset), 47, 18 + (offset), 52, 3, 1, color); \
+ drawHUDLockLine(21 + (offset), 55, 27 + (offset), 61, 3, 1, color); \
+ drawHUDLockLine(27 + (offset), 62, 21 + (offset), 68, 3, 1, color); \
+ drawHUDLockLine(18 + (offset), 71, 13 + (offset), 76, 3, 1, color); \
+ drawHUDLockLine(142 - (offset), 47, 137 - (offset), 52, 3, 1, color); \
+ drawHUDLockLine(134 - (offset), 55, 128 - (offset), 61, 3, 1, color); \
+ drawHUDLockLine(128 - (offset), 62, 134 - (offset), 68, 3, 1, color); \
+ drawHUDLockLine(137 - (offset), 71, 142 - (offset), 76, 3, 1, color)
+
+ShuttleHUD::ShuttleHUD() : DisplayElement(kNoDisplayElement) {
+ _lightGreen = g_system->getScreenFormat().RGBToColor(0, 204, 0);
+ _gridDarkGreen = g_system->getScreenFormat().RGBToColor(0, 85, 0);
+ _lockDarkGreen1 = g_system->getScreenFormat().RGBToColor(0, 68, 0);
+ _lockDarkGreen2 = g_system->getScreenFormat().RGBToColor(0, 65, 0);
+
+ _targetLocked = false;
+ setBounds(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth,
+ kShuttleWindowTop + kShuttleWindowHeight);
+ setDisplayOrder(kShuttleHUDOrder);
+}
+
+void ShuttleHUD::initShuttleHUD() {
+ startDisplaying();
+ startIdling();
+}
+
+void ShuttleHUD::cleanUpShuttleHUD() {
+ stopIdling();
+ stopDisplaying();
+}
+
+void ShuttleHUD::showTargetGrid() {
+ show();
+}
+
+void ShuttleHUD::hideTargetGrid() {
+ hide();
+ unlockOnTarget();
+}
+
+void ShuttleHUD::useIdleTime() {
+ if (isVisible()) {
+ Common::Rect r;
+ g_robotShip->getShuttleBounds(r);
+ if (r.left < kTractorLockRight && r.right > kTractorLockLeft && r.top < kTractorLockBottom && r.bottom > kTractorLockTop)
+ lockOnTarget();
+ else
+ unlockOnTarget();
+ }
+}
+
+void ShuttleHUD::lockOnTarget() {
+ if (!_targetLocked) {
+ _targetLocked = true;
+ triggerRedraw();
+ }
+}
+
+void ShuttleHUD::unlockOnTarget() {
+ if (_targetLocked) {
+ _targetLocked = false;
+ triggerRedraw();
+ }
+}
+
+void ShuttleHUD::draw(const Common::Rect &) {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
+
+ for (int y = 0; y < 35; y++) {
+ Common::Rect r;
+
+ if (y & 1) {
+ if (y == 17) {
+ r = Common::Rect(0, 0, 4, 2);
+ r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 12, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+
+ r = Common::Rect(0, 0, 6, 2);
+ r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _lightGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 8, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _lightGreen);
+
+ r = Common::Rect(0, 0, 23, 2);
+ r.moveTo(kHUDTargetGridLeft + 12, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _lightGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 35, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _lightGreen);
+ } else if (y == 1 || y == 15 || y == 19 || y == 33) {
+ r = Common::Rect(0, 0, 4, 2);
+ r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 6, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+
+ r = Common::Rect(0, 0, 15, 2);
+ r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 23, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ } else {
+ r = Common::Rect(0, 0, 4, 2);
+ r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 6, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+
+ r = Common::Rect(0, 0, 10, 2);
+ r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 18, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ }
+ } else {
+ r = Common::Rect(0, 0, 2, 2);
+ r.moveTo(kHUDTargetGridLeft, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 2, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+
+ r = Common::Rect(0, 0, 4, 2);
+ r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 12, y * 6 + kHUDTargetGridTop);
+ screen->fillRect(r, _gridDarkGreen);
+ }
+ }
+
+ drawOneBitImageOr(screen, s_RS232Data, 2, Common::Rect(kHUDRS232Left, kHUDRS232Top,
+ kHUDRS232Left + 29, kHUDRS232Top + 8), _gridDarkGreen);
+
+ if (_targetLocked) {
+ drawHUDLockArrows(0, _lockDarkGreen2);
+ drawHUDLockArrows(12, _lockDarkGreen1);
+ drawHUDLockArrows(24, _lightGreen);
+ drawOneBitImageOr(screen, s_lockData, 4, Common::Rect(kHUDLockLeft, kHUDLockTop + 115,
+ kHUDLockLeft + 52, kHUDLockTop + 115 + 9), _lightGreen);
+ }
+}
+
+void ShuttleHUD::drawOneBitImageOr(Graphics::Surface *screen, const uint16 *data, int pitch, const Common::Rect &bounds, uint32 color) {
+ for (int y = 0; y < bounds.height(); y++) {
+ for (int x = 0; x < bounds.width(); x++) {
+ if ((data[y * pitch + x / 16] & (1 << (15 - (x % 16)))) != 0) {
+ if (screen->format.bytesPerPixel == 2)
+ WRITE_UINT16((byte *)screen->getBasePtr(x + bounds.left, y + bounds.top), color);
+ else
+ WRITE_UINT32((byte *)screen->getBasePtr(x + bounds.left, y + bounds.top), color);
+ }
+ }
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.h b/engines/pegasus/neighborhood/mars/shuttlehud.h
new file mode 100644
index 0000000000..f7dbbaeae8
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/shuttlehud.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEHUD_H
+#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEHUD_H
+
+#include "pegasus/elements.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class ShuttleHUD : public DisplayElement, public Idler {
+public:
+ ShuttleHUD();
+
+ void showTargetGrid();
+ void hideTargetGrid();
+
+ void initShuttleHUD();
+ void cleanUpShuttleHUD();
+
+ bool isTargetLocked() { return _targetLocked; }
+
+ void draw(const Common::Rect &);
+
+protected:
+ void useIdleTime();
+ void lockOnTarget();
+ void unlockOnTarget();
+ void drawOneBitImageOr(Graphics::Surface *, const uint16 *, int, const Common::Rect &, uint32);
+
+ bool _targetLocked;
+ uint32 _lightGreen, _gridDarkGreen, _lockDarkGreen1, _lockDarkGreen2;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/shuttleweapon.cpp b/engines/pegasus/neighborhood/mars/shuttleweapon.cpp
new file mode 100644
index 0000000000..b4c360b280
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/shuttleweapon.cpp
@@ -0,0 +1,129 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/mars/robotship.h"
+#include "pegasus/neighborhood/mars/shuttleweapon.h"
+#include "pegasus/neighborhood/mars/spacejunk.h"
+
+namespace Pegasus {
+
+ShuttleWeapon::ShuttleWeapon() : IdlerAnimation(kNoDisplayElement) {
+ setScale(kShuttleWeaponScale);
+ _weaponDuration = kShuttleWeaponScale * 2;
+ setSegment(0, _weaponDuration);
+ setBounds(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth,
+ kShuttleWindowTop + kShuttleWindowHeight);
+ setDisplayOrder(kShuttleWeaponFrontOrder);
+}
+
+void ShuttleWeapon::initShuttleWeapon() {
+ startDisplaying();
+}
+
+void ShuttleWeapon::cleanUpShuttleWeapon() {
+ stop();
+ hide();
+ stopDisplaying();
+}
+
+bool ShuttleWeapon::canFireWeapon() {
+ return !isRunning();
+}
+
+void ShuttleWeapon::fireWeapon(const CoordType hStop, const CoordType vStop) {
+ if (!isRunning()) {
+ stop();
+ setTime(0);
+ show();
+
+ Common::Point pt2D(hStop, vStop);
+ project2DTo3D(pt2D, kShuttleDistance, _weaponTarget);
+ _weaponTime = 0;
+ setDisplayOrder(kShuttleWeaponFrontOrder);
+ start();
+ }
+}
+
+void ShuttleWeapon::updateWeaponPosition() {
+ _weaponTime = (float)_lastTime / _weaponDuration;
+ linearInterp(_weaponOrigin, _weaponTarget, _weaponTime, _weaponLocation);
+
+ if (_weaponTime == 1.0) {
+ stop();
+ hide();
+ } else {
+ triggerRedraw();
+ }
+}
+
+void ShuttleWeapon::timeChanged(const TimeValue) {
+ updateWeaponPosition();
+
+ bool hit = false;
+ Common::Point impactPoint;
+
+ if (g_spaceJunk->isJunkFlying()) {
+ hit = collisionWithJunk(impactPoint);
+ if (hit) {
+ stop();
+ hide();
+ hitJunk(impactPoint);
+ }
+ }
+
+ if (!hit && _weaponTime == 1.0 && collisionWithShuttle(impactPoint))
+ hitShuttle(impactPoint);
+}
+
+bool ShuttleWeapon::collisionWithJunk(Common::Point &impactPoint) {
+ if (getDisplayOrder() == kShuttleWeaponFrontOrder) {
+ Point3D junkPosition;
+ g_spaceJunk->getJunkPosition(junkPosition);
+
+ if (junkPosition.z < _weaponLocation.z) {
+ setDisplayOrder(kShuttleWeaponBackOrder);
+ project3DTo2D(_weaponLocation, impactPoint);
+ return g_spaceJunk->pointInJunk(impactPoint);
+ }
+ }
+
+ return false;
+}
+
+bool ShuttleWeapon::collisionWithShuttle(Common::Point &impactPoint) {
+ project3DTo2D(_weaponLocation, impactPoint);
+ return g_robotShip->pointInShuttle(impactPoint);
+}
+
+void ShuttleWeapon::hitJunk(Common::Point impactPoint) {
+ g_spaceJunk->hitByEnergyBeam(impactPoint);
+}
+
+void ShuttleWeapon::hitShuttle(Common::Point impactPoint) {
+ g_robotShip->hitByEnergyBeam(impactPoint);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/shuttleweapon.h b/engines/pegasus/neighborhood/mars/shuttleweapon.h
new file mode 100644
index 0000000000..38529c8919
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/shuttleweapon.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEWEAPON_H
+#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEWEAPON_H
+
+#include "pegasus/elements.h"
+#include "pegasus/neighborhood/mars/spacechase3d.h"
+
+namespace Pegasus {
+
+// Can fire multiple times?
+// For now, no...
+// clone2727 adds: And now forever
+
+static const TimeScale kShuttleWeaponScale = kFifteenTicksPerSecond;
+
+class ShuttleWeapon : public IdlerAnimation {
+public:
+ ShuttleWeapon();
+ virtual ~ShuttleWeapon() {}
+
+ virtual void initShuttleWeapon();
+ virtual void cleanUpShuttleWeapon();
+
+ virtual void fireWeapon(const CoordType, const CoordType);
+
+ bool canFireWeapon();
+
+protected:
+ void timeChanged(const TimeValue);
+ virtual void updateWeaponPosition();
+ virtual bool collisionWithJunk(Common::Point &impactPoint);
+ bool collisionWithShuttle(Common::Point &impactPoint);
+ virtual void hitJunk(Common::Point impactPoint);
+ virtual void hitShuttle(Common::Point impactPoint);
+
+ Point3D _weaponOrigin, _weaponTarget;
+ Point3D _weaponLocation;
+ float _weaponTime;
+ TimeValue _weaponDuration;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/spacechase3d.cpp b/engines/pegasus/neighborhood/mars/spacechase3d.cpp
new file mode 100644
index 0000000000..05f8233763
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/spacechase3d.cpp
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/neighborhood/mars/spacechase3d.h"
+
+namespace Pegasus {
+
+void project3DTo2D(const Point3D &pt3D, Common::Point &pt2D) {
+ pt2D.x = (int)convertSpaceXToScreenH(pt3D.x, pt3D.z);
+ pt2D.y = (int)convertSpaceYToScreenV(pt3D.y, pt3D.z);
+}
+
+void project2DTo3D(const Common::Point &pt2D, const float screenDistance, Point3D &pt3D) {
+ pt3D.x = convertScreenHToSpaceX(pt2D.x, screenDistance);
+ pt3D.y = convertScreenVToSpaceY(pt2D.y, screenDistance);
+ pt3D.z = screenDistance;
+}
+
+void linearInterp(const Point3D &pt1, const Point3D &pt2, const float t, Point3D &pt3) {
+ pt3.x = pt1.x + (pt2.x - pt1.x) * t;
+ pt3.y = pt1.y + (pt2.y - pt1.y) * t;
+ pt3.z = pt1.z + (pt2.z - pt1.z) * t;
+}
+
+void linearInterp(const Point3D &pt1, const float x2, const float y2, const float z2, const float t, Point3D &pt3) {
+ pt3.x = pt1.x + (x2 - pt1.x) * t;
+ pt3.y = pt1.y + (y2 - pt1.y) * t;
+ pt3.z = pt1.z + (z2 - pt1.z) * t;
+}
+
+void linearInterp(const float x1, const float y1, const float z1, const Point3D &pt2, const float t, Point3D &pt3) {
+ pt3.x = x1 + (pt2.x - x1) * t;
+ pt3.y = y1 + (pt2.y - y1) * t;
+ pt3.z = z1 + (pt2.z - z1) * t;
+}
+
+void linearInterp(const float x1, const float y1, const float z1, const float x2, const float y2, const float z2,
+ const float t, Point3D &pt3) {
+ pt3.x = x1 + (x2 - x1) * t;
+ pt3.y = y1 + (y2 - y1) * t;
+ pt3.z = z1 + (z2 - z1) * t;
+}
+
+void linearInterp(const Common::Point &pt1, const Common::Point &pt2, const float t, Common::Point &pt3) {
+ pt3.x = (int)(pt1.x + (pt2.x - pt1.x) * t);
+ pt3.y = (int)(pt1.y + (pt2.y - pt1.y) * t);
+}
+
+void linearInterp(const Common::Point &pt1, const float h2, const float v2, const float t, Common::Point &pt3) {
+ pt3.x = (int)(pt1.x + (h2 - pt1.x) * t);
+ pt3.y = (int)(pt1.y + (v2 - pt1.y) * t);
+}
+
+void linearInterp(const float h1, const float v1, const Common::Point &pt2, const float t, Common::Point &pt3) {
+ pt3.x = (int)(h1 + (pt2.x - h1) * t);
+ pt3.y = (int)(v1 + (pt2.y - v1) * t);
+}
+
+void linearInterp(const float h1, const float v1, const float h2, const float v2, const float t, Common::Point &pt3) {
+ pt3.x = (int)(h1 + (h2 - h1) * t);
+ pt3.y = (int)(v1 + (v2 - v1) * t);
+}
+
+float linearInterp(const float arg1, const float arg2, const float t) {
+ return arg1 + (arg2 - arg1) * t;
+}
+
+bool isNegative(int a) {
+ return a < 0;
+}
+
+bool isPositive(int a) {
+ return a > 0;
+}
+
+int sign(int a) {
+ return isNegative(a) ? -1 : isPositive(a) ? 1 : 0;
+}
+
+bool sameSign(int a, int b) {
+ return sign(a) == sign(b);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/spacechase3d.h b/engines/pegasus/neighborhood/mars/spacechase3d.h
new file mode 100644
index 0000000000..f6815e69bd
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/spacechase3d.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_SPACECHASE3D_H
+#define PEGASUS_NEIGHBORHOOD_MARS_SPACECHASE3D_H
+
+#include "pegasus/neighborhood/mars/constants.h"
+
+namespace Pegasus {
+
+// This is approximately right for a field of view of 72 degrees
+// (Should be set to the tangent of FOV).
+//static const float kTangentFOV = 0.76254;
+static const float kTangentFOV = 1.0;
+
+// Define these as macros and they can be used to define constants...
+#define convertSpaceXToScreenH(x, z) \
+ ((x) / (z) * (kScreenWidth / (2 * kTangentFOV)) + kShuttleWindowMidH)
+
+#define convertSpaceYToScreenV(y, z) \
+ (kShuttleWindowMidV - (y) / (z) * (kScreenWidth / (2 * kTangentFOV)))
+
+#define convertScreenHToSpaceX(x, d) \
+ (((2.0 * kTangentFOV) / kScreenWidth) * ((float)(x) - kShuttleWindowMidH) * (d))
+
+#define convertScreenVToSpaceY(y, d) \
+ (((2.0 * kTangentFOV) / kScreenWidth) * ((float)kShuttleWindowMidV - (y)) * (d))
+
+struct Point3D {
+ float x, y, z;
+
+ Point3D() : x(0), y(0), z(0) {}
+ Point3D(float x1, float y1, float z1) : x(x1), y(y1), z(z1) {}
+ bool operator==(const Point3D &p) const { return x == p.x && y == p.y && z == p.z; }
+ bool operator!=(const Point3D &p) const { return x != p.x || y != p.y || z != p.z; }
+
+ void translate(float dx, float dy, float dz) {
+ x += dx;
+ y += dy;
+ z += dz;
+ }
+};
+
+static const int kScreenWidth = kShuttleWindowWidth;
+
+bool isNegative(int a);
+bool isPositive(int a);
+int sign(int a);
+bool sameSign(int a, int b);
+
+void project3DTo2D(const Point3D &pt3D, Common::Point &pt2D);
+void project2DTo3D(const Common::Point &pt2D, const float screenDistance, Point3D &pt3D);
+
+void linearInterp(const Point3D &pt1, const Point3D &pt2, const float t, Point3D &pt3);
+void linearInterp(const Point3D &pt1, const float x2, const float y2, const float z2, const float t, Point3D &pt3);
+void linearInterp(const float x1, const float y1, const float z1, const Point3D &pt2, const float t, Point3D &pt3);
+void linearInterp(const float x1, const float y1, const float z1, const float x2,
+ const float y2, const float z2, const float t, Point3D &pt3);
+
+void linearInterp(const Common::Point &pt1, const Common::Point &pt2, const float t, Common::Point &pt3);
+void linearInterp(const Common::Point &pt1, const float h2, const float v2, const float t, Common::Point &pt3);
+void linearInterp(const float h1, const float v1, const Common::Point &pt2, const float t, Common::Point &pt3);
+void linearInterp(const float h1, const float v1, const float h2, const float v2, const float t, Common::Point &pt3);
+
+float linearInterp(const float arg1, const float arg2, const float t);
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/spacejunk.cpp b/engines/pegasus/neighborhood/mars/spacejunk.cpp
new file mode 100644
index 0000000000..3912e659c2
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/spacejunk.cpp
@@ -0,0 +1,212 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/mars/mars.h"
+#include "pegasus/neighborhood/mars/spacejunk.h"
+
+namespace Pegasus {
+
+static const CoordType kMaxBounceSize = 90;
+static const CoordType kBounceTargetHRange = 640 - kMaxBounceSize - 2;
+static const CoordType kBounceTargetVRange = 480 - kMaxBounceSize - 2;
+
+static const float kJunkXTarget = 0;
+static const float kJunkYTarget = 0;
+static const float kJunkZTarget = kJunkMinDistance;
+
+SpaceJunk *g_spaceJunk = 0;
+
+SpaceJunk::SpaceJunk(const DisplayElementID id) : ScalingMovie(id) {
+ _timer.setScale(kJunkTimeScale);
+ _bouncing = false;
+ g_spaceJunk = this;
+}
+
+SpaceJunk::~SpaceJunk() {
+ g_spaceJunk = 0;
+}
+
+void SpaceJunk::launchJunk(int16 whichJunk, CoordType xOrigin, CoordType yOrigin) {
+ _bouncing = false;
+ TimeValue startTime = whichJunk * 16 * 40;
+ TimeValue stopTime = startTime + 16 * 40;
+
+ _launchPoint = Point3D(convertScreenHToSpaceX(xOrigin, kJunkMaxDistance),
+ convertScreenVToSpaceY(yOrigin, kJunkMaxDistance), kJunkMaxDistance);
+ startIdling();
+ stop();
+ setFlags(0);
+ setSegment(startTime, stopTime);
+ setFlags(kLoopTimeBase);
+ setTime(startTime);
+ start();
+ show();
+ _timer.stop();
+ _timer.setSegment(0, kJunkTravelTime);
+ _timer.setTime(0);
+
+ // Force it to set up correctly from the get-go
+ useIdleTime();
+
+ _timer.start();
+}
+
+void SpaceJunk::setCenter(const CoordType centerX, const CoordType centerY) {
+ _center.x = centerX;
+ _center.y = centerY;
+
+ Common::Rect r;
+ getBounds(r);
+ r.moveTo(CLIP<int>(centerX - (r.width() >> 1), 0, 640 - r.width()), CLIP<int>(centerY - (r.height() >> 1), 0, 480 - r.height()));
+ setBounds(r);
+}
+
+void SpaceJunk::setScaleSize(const CoordType size) {
+ Common::Rect r;
+ r.left = _center.x - (size >> 1);
+ r.top = _center.y - (size >> 1);
+ r.right = r.left + size;
+ r.bottom = r.top + size;
+ setBounds(r);
+}
+
+void SpaceJunk::useIdleTime() {
+ if (_bouncing) {
+ TimeValue time = _timer.getTime();
+ Common::Point pt;
+ pt.x = linearInterp(0, _bounceTime, time, _bounceStart.x, _bounceStop.x);
+ pt.y = linearInterp(0, _bounceTime, time, _bounceStart.y, _bounceStop.y);
+ CoordType size = linearInterp(0, _bounceTime, time, _bounceSizeStart, _bounceSizeStop);
+ setCenter(pt.x, pt.y);
+ setScaleSize(size);
+
+ if (time == _bounceTime) {
+ stop();
+ stopIdling();
+ hide();
+ ((Mars *)g_neighborhood)->setUpNextDropTime();
+ }
+ } else {
+ float t = (float)_timer.getTime() / kJunkTravelTime;
+ linearInterp(_launchPoint, kJunkXTarget, kJunkYTarget, kJunkZTarget, t, _junkPosition);
+
+ Common::Point pt2D;
+ project3DTo2D(_junkPosition, pt2D);
+ setCenter(pt2D.x, pt2D.y);
+ setScaleSize((int)(convertSpaceYToScreenV(_junkPosition.y - kJunkSize / 2, _junkPosition.z) -
+ convertSpaceYToScreenV(_junkPosition.y + kJunkSize / 2, _junkPosition.z)));
+
+ if (t == 1.0) {
+ rebound(kCollisionReboundTime);
+ ((Mars *)g_neighborhood)->hitByJunk();
+ }
+ }
+}
+
+bool SpaceJunk::pointInJunk(const Common::Point &pt) {
+ Common::Rect r;
+ getBounds(r);
+
+ int dx = r.width() / 4;
+ int dy = r.height() / 4;
+
+ r.left += dx;
+ r.right -= dx;
+ r.top += dy;
+ r.top -= dy;
+
+ return r.contains(pt);
+}
+
+void SpaceJunk::rebound(const TimeValue reboundTime) {
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ _bounceStart.x = (bounds.left + bounds.right) >> 1;
+ _bounceStart.y = (bounds.top + bounds.bottom) >> 1;
+
+ PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+ switch (vm->getRandomNumber(3)) {
+ case 0:
+ _bounceStop.x = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetHRange - 1);
+ _bounceStop.y = kMaxBounceSize / 2 + 1;
+ break;
+ case 1:
+ _bounceStop.x = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetHRange - 1);
+ _bounceStop.y = 480 - kMaxBounceSize / 2 + 1;
+ break;
+ case 2:
+ _bounceStop.x = kMaxBounceSize / 2 + 1;
+ _bounceStop.y = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetVRange - 1);
+ break;
+ case 3:
+ _bounceStop.x = 640 - kMaxBounceSize / 2 + 1;
+ _bounceStop.y = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetVRange - 1);
+ break;
+ }
+
+ _bounceSizeStart = bounds.width();
+ _bounceSizeStop = MIN(_bounceSizeStart, kMaxBounceSize);
+
+ _timer.stop();
+ _timer.setSegment(0, reboundTime);
+ _bounceTime = reboundTime;
+ _timer.setTime(0);
+ _timer.start();
+
+ _bouncing = true;
+}
+
+void SpaceJunk::hitByEnergyBeam(Common::Point) {
+ rebound(kWeaponReboundTime);
+ setGlowing(true);
+ ((PegasusEngine *)g_engine)->delayShell(1, 3);
+ setGlowing(false);
+}
+
+void SpaceJunk::hitByGravitonCannon(Common::Point impactPoint) {
+ stop();
+ stopIdling();
+ hide();
+
+ Common::Rect r;
+ getBounds(r);
+ r = Common::Rect::center(impactPoint.x, impactPoint.y, r.width(), r.height());
+
+ ((Mars *)g_neighborhood)->showBigExplosion(r, kShuttleJunkOrder);
+ ((Mars *)g_neighborhood)->setUpNextDropTime();
+}
+
+void SpaceJunk::getJunkPosition(Point3D &position) {
+ position = _junkPosition;
+}
+
+bool SpaceJunk::isJunkFlying() {
+ return isIdling();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/spacejunk.h b/engines/pegasus/neighborhood/mars/spacejunk.h
new file mode 100644
index 0000000000..2ec9192513
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/spacejunk.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_SPACEJUNK_H
+#define PEGASUS_NEIGHBORHOOD_MARS_SPACEJUNK_H
+
+#include "pegasus/movie.h"
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/mars/spacechase3d.h"
+
+namespace Pegasus {
+
+static const CoordType kJunkMaxScreenSize = 250;
+
+static const float kJunkSize = convertScreenVToSpaceY(kShuttleWindowMidV - kJunkMaxScreenSize / 2, kJunkMinDistance) -
+ convertScreenVToSpaceY(kShuttleWindowMidV + kJunkMaxScreenSize / 2, kJunkMinDistance);
+
+class SpaceJunk : public ScalingMovie, public Idler {
+public:
+ SpaceJunk(const DisplayElementID);
+ virtual ~SpaceJunk();
+
+ void setCenter(const CoordType, const CoordType);
+ void setScaleSize(const CoordType);
+
+ void useIdleTime();
+
+ void launchJunk(int16, CoordType, CoordType);
+
+ void getJunkPosition(Point3D &);
+ bool isJunkFlying();
+
+ bool pointInJunk(const Common::Point &);
+
+ void hitByEnergyBeam(Common::Point impactPoint);
+ void hitByGravitonCannon(Common::Point impactPoint);
+
+ bool junkFlying() { return _timer.isRunning(); }
+
+protected:
+ void rebound(const TimeValue);
+
+ TimeBase _timer;
+ Point3D _launchPoint, _junkPosition;
+ Common::Point _center;
+ bool _bouncing;
+ Common::Point _bounceStart, _bounceStop;
+ CoordType _bounceSizeStart, _bounceSizeStop;
+ TimeValue _bounceTime;
+};
+
+extern SpaceJunk *g_spaceJunk;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/tractorbeam.cpp b/engines/pegasus/neighborhood/mars/tractorbeam.cpp
new file mode 100644
index 0000000000..d3f9c94328
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/tractorbeam.cpp
@@ -0,0 +1,139 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/mars/tractorbeam.h"
+
+namespace Pegasus {
+
+TractorBeam::TractorBeam() : DisplayElement(kNoDisplayElement) {
+ setBounds(kShuttleTractorLeft, kShuttleTractorTop, kShuttleTractorLeft + kShuttleTractorWidth,
+ kShuttleTractorTop + kShuttleTractorHeight);
+ setDisplayOrder(kShuttleTractorBeamOrder);
+
+}
+
+static const int kHalfWidth = kShuttleTractorWidth >> 1;
+static const int kHalfHeight = kShuttleTractorHeight >> 1;
+
+static const int kW3Vert = kHalfHeight * kHalfHeight * kHalfHeight;
+static const int kW3Div2Vert = kW3Vert >> 1;
+
+static const int kW3Horiz = kHalfWidth * kHalfWidth * kHalfWidth;
+static const int kW3Div2Horiz = kW3Horiz >> 1;
+
+static const int kMaxLevel = 50;
+
+static const int kAVert = -2 * kMaxLevel;
+static const int kBVert = 3 * kMaxLevel * kHalfHeight;
+
+#define READ_PIXEL(ptr) \
+ if (screen->format.bytesPerPixel == 2) \
+ color = READ_UINT16(ptr); \
+ else \
+ color = READ_UINT32(ptr); \
+ screen->format.colorToRGB(color, r, g, b)
+
+#define WRITE_PIXEL(ptr) \
+ color = screen->format.RGBToColor(r, g, b); \
+ if (screen->format.bytesPerPixel == 2) \
+ WRITE_UINT16(ptr, color); \
+ else \
+ WRITE_UINT32(ptr, color)
+
+#define DO_BLEND(ptr) \
+ READ_PIXEL(ptr); \
+ g += (((0xff - g) * blendHoriz) >> 8); \
+ b += (((0xff - b) * blendHoriz) >> 8); \
+ WRITE_PIXEL(ptr)
+
+void TractorBeam::draw(const Common::Rect &) {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
+
+ // Set up vertical DDA.
+ int blendVert = 0;
+ int dVert = 0;
+ int d1Vert = kAVert + kBVert;
+ int d2Vert = 6 * kAVert + 2 * kBVert;
+ int d3Vert = 6 * kAVert;
+
+ byte *rowPtrTop = (byte *)screen->getBasePtr(_bounds.left, _bounds.top);
+ byte *rowPtrBottom = (byte *)screen->getBasePtr(_bounds.left, _bounds.top + ((kHalfHeight << 1) - 1));
+
+ for (int y = kHalfHeight; y > 0; y--) {
+ // Set up horizontal DDA
+ int A = -2 * blendVert;
+ int B = 3 * blendVert * kHalfWidth;
+ int blendHoriz = 0;
+ int dHoriz = 0;
+ int d1Horiz = A + B;
+ int d2Horiz = 6 * A + 2 * B;
+ int d3Horiz = 6 * A;
+
+ byte *pTopLeft = rowPtrTop;
+ byte *pTopRight = rowPtrTop + (kHalfWidth * 2 - 1) * screen->format.bytesPerPixel;
+ byte *pBottomLeft = rowPtrBottom;
+ byte *pBottomRight = rowPtrBottom + (kHalfWidth * 2 - 1) * screen->format.bytesPerPixel;
+
+ for (int x = kHalfWidth; x > 0; x--) {
+ byte r, g, b;
+ uint32 color;
+
+ DO_BLEND(pTopLeft);
+ DO_BLEND(pTopRight);
+ DO_BLEND(pBottomLeft);
+ DO_BLEND(pBottomRight);
+
+ pTopLeft += screen->format.bytesPerPixel;
+ pBottomLeft += screen->format.bytesPerPixel;
+ pTopRight -= screen->format.bytesPerPixel;
+ pBottomRight -= screen->format.bytesPerPixel;
+
+ while (dHoriz > kW3Div2Horiz) {
+ blendHoriz++;
+ dHoriz -= kW3Horiz;
+ }
+
+ dHoriz += d1Horiz;
+ d1Horiz += d2Horiz;
+ d2Horiz += d3Horiz;
+ }
+
+ rowPtrTop += screen->pitch;
+ rowPtrBottom -= screen->pitch;
+
+ while (dVert > kW3Div2Vert) {
+ blendVert++;
+ dVert -= kW3Vert;
+ }
+
+ dVert += d1Vert;
+ d1Vert += d2Vert;
+ d2Vert += d3Vert;
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/tractorbeam.h b/engines/pegasus/neighborhood/mars/tractorbeam.h
new file mode 100644
index 0000000000..cd87992d11
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/tractorbeam.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_TRACTORBEAM_H
+#define PEGASUS_NEIGHBORHOOD_MARS_TRACTORBEAM_H
+
+#include "pegasus/elements.h"
+
+namespace Pegasus {
+
+class TractorBeam : public DisplayElement {
+public:
+ TractorBeam();
+ virtual ~TractorBeam() {}
+
+ void draw(const Common::Rect &);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/neighborhood.cpp b/engines/pegasus/neighborhood/neighborhood.cpp
new file mode 100644
index 0000000000..07be62c957
--- /dev/null
+++ b/engines/pegasus/neighborhood/neighborhood.cpp
@@ -0,0 +1,1774 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+
+#include "pegasus/compass.h"
+#include "pegasus/cursor.h"
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/graphics.h"
+#include "pegasus/input.h"
+#include "pegasus/interaction.h"
+#include "pegasus/interface.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/mapchip.h"
+#include "pegasus/neighborhood/neighborhood.h"
+#include "pegasus/neighborhood/tsa/fulltsa.h"
+#include "pegasus/neighborhood/tsa/tinytsa.h"
+
+namespace Pegasus {
+
+StriderCallBack::StriderCallBack(Neighborhood *neighborhood) {
+ _neighborhood = neighborhood;
+}
+
+void StriderCallBack::callBack() {
+ _neighborhood->checkStriding();
+}
+
+static const TimeValue kStridingSlop = 39;
+
+Neighborhood *g_neighborhood = 0;
+
+Neighborhood::Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id)
+ : InputHandler(nextHandler), IDObject(id), _vm(vm), _resName(resName), _navMovie(kNavMovieID), _stridingCallBack(this),
+ _neighborhoodNotification(kNeighborhoodNotificationID, (NotificationManager *)vm), _pushIn(kNoDisplayElement),
+ _turnPush(kTurnPushID), _croppedMovie(kCroppedMovieID) {
+ GameState.setOpenDoorLocation(kNoRoomID, kNoDirection);
+ _currentAlternate = 0;
+ _currentActivation = kActivateHotSpotAlways;
+ _interruptionFilter = kFilterAllInput;
+ allowInput(true);
+ resetLastExtra();
+ g_neighborhood = this;
+ _currentInteraction = 0;
+ _doneWithInteraction = false;
+ _croppedMovie.setDisplayOrder(kCroppedMovieLayer);
+}
+
+Neighborhood::~Neighborhood() {
+ for (HotspotIterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++)
+ _vm->getAllHotspots().remove(*it);
+
+ _neighborhoodHotspots.deleteHotspots();
+ g_neighborhood = 0;
+
+ loadLoopSound1("");
+ loadLoopSound2("");
+ newInteraction(kNoInteractionID);
+
+ if (g_AIArea)
+ g_AIArea->removeAllRules();
+}
+
+void Neighborhood::init() {
+ _neighborhoodNotification.notifyMe(this, kNeighborhoodFlags, kNeighborhoodFlags);
+ _navMovieCallBack.setNotification(&_neighborhoodNotification);
+ _turnPushCallBack.setNotification(&_neighborhoodNotification);
+ _delayCallBack.setNotification(&_neighborhoodNotification);
+ _spotSoundCallBack.setNotification(&_neighborhoodNotification);
+
+ debug(0, "Loading '%s' neighborhood resources", _resName.c_str());
+
+ Common::SeekableReadStream *stream = _vm->_resFork->getResource(_doorTable.getResTag(), _resName);
+ if (!stream)
+ error("Failed to load doors");
+ _doorTable.loadFromStream(stream);
+ delete stream;
+
+ stream = _vm->_resFork->getResource(_exitTable.getResTag(), _resName);
+ if (!stream)
+ error("Failed to load exits");
+ _exitTable.loadFromStream(stream);
+ delete stream;
+
+ stream = _vm->_resFork->getResource(_extraTable.getResTag(), _resName);
+ if (!stream)
+ error("Failed to load extras");
+ _extraTable.loadFromStream(stream);
+ delete stream;
+
+ stream = _vm->_resFork->getResource(_hotspotInfoTable.getResTag(), _resName);
+ if (!stream)
+ error("Failed to load hotspot info");
+ _hotspotInfoTable.loadFromStream(stream);
+ delete stream;
+
+ stream = _vm->_resFork->getResource(_spotTable.getResTag(), _resName);
+ if (!stream)
+ error("Failed to load spots");
+ _spotTable.loadFromStream(stream);
+ delete stream;
+
+ stream = _vm->_resFork->getResource(_turnTable.getResTag(), _resName);
+ if (!stream)
+ error("Failed to load turns");
+ _turnTable.loadFromStream(stream);
+ delete stream;
+
+ stream = _vm->_resFork->getResource(_viewTable.getResTag(), _resName);
+ if (!stream)
+ error("Failed to load views");
+ _viewTable.loadFromStream(stream);
+ delete stream;
+
+ stream = _vm->_resFork->getResource(_zoomTable.getResTag(), _resName);
+ if (!stream)
+ error("Failed to load zooms");
+ _zoomTable.loadFromStream(stream);
+ delete stream;
+
+ createNeighborhoodSpots();
+
+ _navMovie.initFromMovieFile(getNavMovieName());
+ _navMovie.setVolume(_vm->getSoundFXLevel());
+
+ Common::String soundSpotsName = getSoundSpotsName();
+ if (soundSpotsName.empty()) {
+ _spotSounds.disposeSound();
+ } else {
+ _spotSounds.initFromQuickTime(getSoundSpotsName());
+ _spotSounds.setVolume(_vm->getSoundFXLevel());
+ }
+
+ _navMovie.setDisplayOrder(kNavMovieOrder);
+ _navMovie.startDisplaying();
+
+ Common::Rect bounds;
+ _navMovie.getBounds(bounds);
+ _pushIn.allocateSurface(bounds);
+
+ _turnPush.setInAndOutElements(&_pushIn, &_navMovie);
+ _turnPush.setDisplayOrder(kTurnPushOrder);
+ _turnPush.startDisplaying();
+ _navMovieCallBack.initCallBack(&_navMovie, kCallBackAtExtremes);
+ _stridingCallBack.initCallBack(&_navMovie, kCallBackAtTime);
+ _turnPushCallBack.initCallBack(&_turnPush, kCallBackAtExtremes);
+ _delayCallBack.initCallBack(&_delayTimer, kCallBackAtExtremes);
+ _spotSoundCallBack.initCallBack(&_spotSounds, kCallBackAtExtremes);
+
+ setUpAIRules();
+
+ if (g_compass)
+ g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
+
+ _soundLoop1.attachFader(&_loop1Fader);
+ _soundLoop2.attachFader(&_loop2Fader);
+ startIdling();
+}
+
+void Neighborhood::start() {
+ GameState.setCurrentRoom(GameState.getLastRoom());
+ GameState.setCurrentDirection(GameState.getLastDirection());
+ arriveAt(GameState.getNextRoom(), GameState.getNextDirection());
+}
+
+void Neighborhood::receiveNotification(Notification *, const NotificationFlags flags) {
+ if ((flags & (kNeighborhoodMovieCompletedFlag | kTurnCompletedFlag)) != 0 && g_AIArea)
+ g_AIArea->unlockAI();
+ if (flags & kMoveForwardCompletedFlag)
+ arriveAt(GameState.getNextRoom(), GameState.getNextDirection());
+ if (flags & kTurnCompletedFlag)
+ turnTo(GameState.getNextDirection());
+ if (flags & kSpotCompletedFlag)
+ spotCompleted();
+ if (flags & kDoorOpenCompletedFlag)
+ doorOpened();
+ if (flags & kActionRequestCompletedFlag)
+ popActionQueue();
+ if (flags & kDeathExtraCompletedFlag)
+ die(_extraDeathReason);
+}
+
+void Neighborhood::arriveAt(const RoomID room, const DirectionConstant direction) {
+ if (g_map)
+ g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), room, direction);
+
+ GameState.setCurrentNeighborhood(getObjectID());
+
+ _currentActivation = kActivateHotSpotAlways;
+ _interruptionFilter = kFilterAllInput;
+
+ if (room != GameState.getCurrentRoom() || direction != GameState.getCurrentDirection()) {
+ GameState.setCurrentRoom(room);
+ GameState.setCurrentDirection(direction);
+ loadAmbientLoops();
+ activateCurrentView(room, direction, kSpotOnArrivalMask);
+ } else {
+ loadAmbientLoops();
+ showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
+ }
+
+ if (GameState.getOpenDoorRoom() != kNoRoomID) {
+ // Arriving always closes a door.
+ loadAmbientLoops();
+ closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection());
+ GameState.setOpenDoorLocation(kNoRoomID, kNoDirection);
+ }
+
+ if (g_compass)
+ g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
+
+ if (g_AIArea)
+ g_AIArea->checkMiddleArea();
+
+ checkContinuePoint(room, direction);
+}
+
+// These functions can be overridden to tweak the exact frames used.
+
+void Neighborhood::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) {
+ entry = _exitTable.findEntry(room, direction, _currentAlternate);
+
+ if (entry.isEmpty())
+ entry = _exitTable.findEntry(room, direction, kNoAlternateID);
+}
+
+TimeValue Neighborhood::getViewTime(const RoomID room, const DirectionConstant direction) {
+ if (GameState.getOpenDoorRoom() == room && GameState.getOpenDoorDirection() == direction) {
+ // If we get here, the door entry for this location must exist.
+ DoorTable::Entry doorEntry = _doorTable.findEntry(room, direction, _currentAlternate);
+
+ if (doorEntry.isEmpty())
+ doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID);
+
+ return doorEntry.movieEnd - 1;
+ }
+
+ ViewTable::Entry viewEntry = _viewTable.findEntry(room, direction, _currentAlternate);
+
+ if (viewEntry.isEmpty())
+ viewEntry = _viewTable.findEntry(room, direction, kNoAlternateID);
+
+ return viewEntry.time;
+}
+
+void Neighborhood::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry) {
+ doorEntry = _doorTable.findEntry(room, direction, _currentAlternate);
+
+ if (doorEntry.isEmpty())
+ doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID);
+}
+
+DirectionConstant Neighborhood::getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turnDirection) {
+ TurnTable::Entry turnEntry = _turnTable.findEntry(room, direction, turnDirection, _currentAlternate);
+
+ if (turnEntry.isEmpty())
+ turnEntry = _turnTable.findEntry(room, direction, turnDirection, kNoAlternateID);
+
+ return turnEntry.endDirection;
+}
+
+void Neighborhood::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) {
+ spotEntry = _spotTable.findEntry(room, direction, flags, _currentAlternate);
+
+ if (spotEntry.isEmpty())
+ spotEntry = _spotTable.findEntry(room, direction, flags, kNoAlternateID);
+}
+
+void Neighborhood::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) {
+ zoomEntry = _zoomTable.findEntry(id);
+}
+
+void Neighborhood::getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry) {
+ hotspotEntry = _hotspotInfoTable.findEntry(id);
+}
+
+void Neighborhood::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) {
+ extraEntry = _extraTable.findEntry(id);
+}
+
+/////////////////////////////////////////////
+//
+// "Can" functions: Called to see whether or not a user is allowed to do something
+
+CanMoveForwardReason Neighborhood::canMoveForward(ExitTable::Entry &entry) {
+ DoorTable::Entry door;
+
+ getExitEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry);
+ getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), door);
+
+ // Fixed this so that doors that don't lead anywhere can be opened, but not walked
+ // through.
+ if (door.flags & kDoorPresentMask) {
+ if (GameState.isCurrentDoorOpen()) {
+ if (entry.exitRoom == kNoRoomID)
+ return kCantMoveBlocked;
+ else
+ return kCanMoveForward;
+ } else if (door.flags & kDoorLockedMask) {
+ return kCantMoveDoorLocked;
+ } else {
+ return kCantMoveDoorClosed;
+ }
+ } else if (entry.exitRoom == kNoRoomID) {
+ return kCantMoveBlocked;
+ }
+
+ return kCanMoveForward;
+}
+
+CanTurnReason Neighborhood::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) {
+ nextDir = getTurnEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), turnDirection);
+
+ if (nextDir == kNoDirection)
+ return kCantTurnNoTurn;
+
+ return kCanTurn;
+}
+
+CanOpenDoorReason Neighborhood::canOpenDoor(DoorTable::Entry &entry) {
+ getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry);
+
+ if (entry.flags & kDoorPresentMask) {
+ if (GameState.isCurrentDoorOpen())
+ return kCantOpenAlreadyOpen;
+
+ if (entry.flags & kDoorLockedMask)
+ return kCantOpenLocked;
+
+ return kCanOpenDoor;
+ }
+
+ return kCantOpenNoDoor;
+}
+
+void Neighborhood::createNeighborhoodSpots() {
+ Common::SeekableReadStream *hotspotList = _vm->_resFork->getResource(MKTAG('H', 'S', 'L', 's'), _resName);
+ if (!hotspotList)
+ error("Could not load neighborhood hotspots");
+
+ uint32 hotspotCount = hotspotList->readUint32BE();
+
+ while (hotspotCount--) {
+ uint16 id = hotspotList->readUint16BE();
+ uint32 flags = hotspotList->readUint32BE();
+ uint32 rgnSize = hotspotList->readUint32BE();
+
+ int32 startPos = hotspotList->pos();
+
+ debug(0, "Hotspot %d:", id);
+ Region region(hotspotList);
+
+ hotspotList->seek(startPos + rgnSize);
+
+ Hotspot *hotspot = new Hotspot(id);
+ hotspot->setHotspotFlags(flags);
+ hotspot->setArea(region);
+
+ _vm->getAllHotspots().push_back(hotspot);
+ _neighborhoodHotspots.push_back(hotspot);
+ }
+
+ delete hotspotList;
+}
+
+void Neighborhood::popActionQueue() {
+ if (!_actionQueue.empty()) {
+ QueueRequest topRequest = _actionQueue.pop();
+
+ switch (topRequest.requestType) {
+ case kNavExtraRequest:
+ _navMovie.stop();
+ break;
+ case kSpotSoundRequest:
+ _spotSounds.stopSound();
+ break;
+ case kDelayRequest:
+ _delayTimer.stop();
+ break;
+ }
+
+ serviceActionQueue();
+ }
+}
+
+void Neighborhood::serviceActionQueue() {
+ if (!_actionQueue.empty()) {
+ QueueRequest &topRequest = _actionQueue.front();
+
+ if (!topRequest.playing) {
+ topRequest.playing = true;
+ switch (topRequest.requestType) {
+ case kNavExtraRequest:
+ startExtraSequence(topRequest.extra, topRequest.flags, topRequest.interruptionFilter);
+ break;
+ case kSpotSoundRequest:
+ _spotSounds.stopSound();
+ _spotSounds.playSoundSegment(topRequest.start, topRequest.stop);
+ _interruptionFilter = topRequest.interruptionFilter;
+ _spotSoundCallBack.setCallBackFlag(topRequest.flags);
+ _spotSoundCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ break;
+ case kDelayRequest:
+ _delayTimer.stop();
+ _delayCallBack.setCallBackFlag(topRequest.flags);
+ _delayTimer.setSegment(0, topRequest.start, topRequest.stop);
+ _delayTimer.setTime(0);
+ _delayCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _interruptionFilter = topRequest.interruptionFilter;
+ _delayTimer.start();
+ break;
+ }
+ }
+ } else {
+ _interruptionFilter = kFilterAllInput;
+ }
+}
+
+void Neighborhood::requestAction(const QueueRequestType requestType, const ExtraID extra, const TimeValue in, const TimeValue out,
+ const InputBits interruptionFilter, const NotificationFlags flags) {
+
+ QueueRequest request;
+
+ request.requestType = requestType;
+ request.extra = extra;
+ request.start = in;
+ request.stop = out;
+ request.interruptionFilter = interruptionFilter;
+ request.playing = false;
+ request.flags = flags | kActionRequestCompletedFlag;
+ request.notification = &_neighborhoodNotification;
+ _actionQueue.push(request);
+ if (_actionQueue.size() == 1)
+ serviceActionQueue();
+}
+
+void Neighborhood::requestExtraSequence(const ExtraID whichExtra, const NotificationFlags flags, const InputBits interruptionFilter) {
+ requestAction(kNavExtraRequest, whichExtra, 0, 0, interruptionFilter, flags);
+}
+
+void Neighborhood::requestSpotSound(const TimeValue in, const TimeValue out, const InputBits interruptionFilter, const NotificationFlags flags) {
+ requestAction(kSpotSoundRequest, 0xFFFFFFFF, in, out, interruptionFilter, flags);
+}
+
+void Neighborhood::playSpotSoundSync(const TimeValue in, const TimeValue out) {
+ // Let the action queue play out first...
+ while (!actionQueueEmpty()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->checkNotifications();
+ _vm->_system->delayMillis(10);
+ }
+
+ _spotSounds.stopSound();
+ _spotSounds.playSoundSegment(in, out);
+
+ while (_spotSounds.isPlaying()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+}
+
+void Neighborhood::requestDelay(const TimeValue delayDuration, const TimeScale delayScale, const InputBits interruptionFilter, const NotificationFlags flags) {
+ requestAction(kDelayRequest, 0xFFFFFFFF, delayDuration, delayScale, interruptionFilter, flags);
+}
+
+bool operator==(const QueueRequest &arg1, const QueueRequest &arg2) {
+ return arg1.requestType == arg2.requestType && arg1.extra == arg2.extra &&
+ arg1.start == arg2.start && arg1.stop == arg2.stop;
+}
+
+bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2) {
+ return !operator==(arg1, arg2);
+}
+
+Common::String Neighborhood::getBriefingMovie() {
+ if (_currentInteraction)
+ return _currentInteraction->getBriefingMovie();
+
+ return Common::String();
+}
+
+Common::String Neighborhood::getEnvScanMovie() {
+ if (_currentInteraction)
+ return _currentInteraction->getEnvScanMovie();
+
+ return Common::String();
+}
+
+uint Neighborhood::getNumHints() {
+ if (_currentInteraction)
+ return _currentInteraction->getNumHints();
+
+ return 0;
+}
+
+Common::String Neighborhood::getHintMovie(uint hintNum) {
+ if (_currentInteraction)
+ return _currentInteraction->getHintMovie(hintNum);
+
+ return Common::String();
+}
+
+bool Neighborhood::canSolve() {
+ if (_currentInteraction)
+ return _currentInteraction->canSolve();
+
+ return false;
+}
+
+void Neighborhood::doSolve() {
+ if (_currentInteraction)
+ _currentInteraction->doSolve();
+}
+
+bool Neighborhood::okayToJump() {
+ return !_vm->playerHasItemID(kGasCanister) && !_vm->playerHasItemID(kMachineGun);
+}
+
+AirQuality Neighborhood::getAirQuality(const RoomID) {
+ return kAirQualityGood;
+}
+
+void Neighborhood::checkStriding() {
+ if (stillMoveForward()) {
+ ExitTable::Entry nextExit;
+ getExitEntry(GameState.getNextRoom(), GameState.getNextDirection(), nextExit);
+ keepStriding(nextExit);
+ } else {
+ stopStriding();
+ }
+}
+
+bool Neighborhood::stillMoveForward() {
+ Input input;
+
+ InputHandler::readInputDevice(input);
+ return input.upButtonAnyDown();
+}
+
+void Neighborhood::keepStriding(ExitTable::Entry &nextExitEntry) {
+ FaderMoveSpec compassMove;
+
+ if (g_map)
+ g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection());
+
+ if (g_compass)
+ getExitCompassMove(nextExitEntry, compassMove);
+
+ GameState.setCurrentRoom(GameState.getNextRoom());
+ GameState.setCurrentDirection(GameState.getNextDirection());
+ GameState.setNextRoom(nextExitEntry.exitRoom);
+ GameState.setNextDirection(nextExitEntry.exitDirection);
+
+ if (nextExitEntry.movieEnd == nextExitEntry.exitEnd)
+ scheduleNavCallBack(kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag);
+ else
+ scheduleStridingCallBack(nextExitEntry.movieEnd - kStridingSlop, kStrideCompletedFlag);
+
+ if (g_compass)
+ g_compass->startFader(compassMove);
+}
+
+void Neighborhood::stopStriding() {
+ _navMovie.stop();
+ _neighborhoodNotification.setNotificationFlags(kNeighborhoodMovieCompletedFlag |
+ kMoveForwardCompletedFlag, kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag);
+}
+
+// Compass support
+int16 Neighborhood::getStaticCompassAngle(const RoomID, const DirectionConstant dir) {
+ // North, south, east, west
+ static const int16 compassAngles[] = { 0, 180, 90, 270 };
+ return compassAngles[dir];
+}
+
+void Neighborhood::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) {
+ int32 startAngle = getStaticCompassAngle(exitEntry.room, exitEntry.direction);
+ int32 stopAngle = getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection);
+
+ if (startAngle > stopAngle) {
+ if (stopAngle + 180 < startAngle)
+ stopAngle += 360;
+ } else {
+ if (startAngle + 180 < stopAngle)
+ startAngle += 360;
+ }
+
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, startAngle, exitEntry.movieEnd, stopAngle);
+}
+
+void Neighborhood::scheduleNavCallBack(NotificationFlags flags) {
+ _navMovieCallBack.cancelCallBack();
+
+ if (flags != 0) {
+ _navMovieCallBack.setCallBackFlag(flags);
+ _navMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+}
+
+void Neighborhood::scheduleStridingCallBack(const TimeValue strideStop, NotificationFlags flags) {
+ _stridingCallBack.cancelCallBack();
+
+ if (flags != 0)
+ _stridingCallBack.scheduleCallBack(kTriggerTimeFwd, strideStop, _navMovie.getScale());
+}
+
+void Neighborhood::moveNavTo(const CoordType h, const CoordType v) {
+ CoordType oldH, oldV;
+ _navMovie.getLocation(oldH, oldV);
+
+ CoordType offH = h - oldH;
+ CoordType offV = v - oldV;
+
+ _navMovie.moveElementTo(h, v);
+ _turnPush.moveElementTo(h, v);
+
+ if (offH != 0 || offV != 0)
+ for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++)
+ if ((*it)->getHotspotFlags() & kNeighborhoodSpotFlag)
+ (*it)->moveSpot(offH, offV);
+}
+
+void Neighborhood::activateHotspots() {
+ InputHandler::activateHotspots();
+
+ for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) {
+ HotspotInfoTable::Entry entry = *it;
+
+ if (entry.hotspotRoom == GameState.getCurrentRoom() && entry.hotspotDirection == GameState.getCurrentDirection()
+ && (entry.hotspotActivation == _currentActivation || entry.hotspotActivation == kActivateHotSpotAlways)) {
+ Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(entry.hotspot);
+ if (hotspot)
+ activateOneHotspot(entry, hotspot);
+ }
+ }
+}
+
+void Neighborhood::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+ HotSpotFlags flags = clickedSpot->getHotspotFlags();
+
+ if ((flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) != 0) {
+ ItemID itemID = kNoItemID;
+
+ for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) {
+ if (it->hotspot == clickedSpot->getObjectID()) {
+ itemID = it->hotspotItem;
+ break;
+ }
+ }
+
+ if (itemID != kNoItemID) {
+ Item *draggingItem = _vm->getAllItems().findItemByID(itemID);
+
+ if (draggingItem) {
+ takeItemFromRoom(draggingItem);
+
+ if ((flags & kPickUpItemSpotFlag) != 0)
+ _vm->dragItem(input, draggingItem, kDragInventoryPickup);
+ else
+ _vm->dragItem(input, draggingItem, kDragBiochipPickup);
+ }
+ }
+ } else {
+ // Check other flags here?
+ if ((flags & kZoomSpotFlags) != 0) {
+ zoomTo(clickedSpot);
+ } else if ((flags & kPlayExtraSpotFlag) != 0) {
+ HotspotInfoTable::Entry hotspotEntry;
+ getHotspotEntry(clickedSpot->getObjectID(), hotspotEntry);
+ startExtraSequence(hotspotEntry.hotspotExtra, kExtraCompletedFlag, kFilterNoInput);
+ } else if ((flags & kOpenDoorSpotFlag) != 0) {
+ openDoor();
+ } else {
+ InputHandler::clickInHotspot(input, clickedSpot);
+ }
+ }
+}
+
+void Neighborhood::cantMoveThatWay(CanMoveForwardReason reason) {
+ switch (reason) {
+ case kCantMoveDoorClosed:
+ case kCantMoveDoorLocked:
+ openDoor();
+ break;
+ case kCantMoveBlocked:
+ zoomUpOrBump();
+ break;
+ default:
+ bumpIntoWall();
+ break;
+ }
+}
+
+void Neighborhood::cantOpenDoor(CanOpenDoorReason) {
+ bumpIntoWall();
+}
+
+void Neighborhood::turnTo(const DirectionConstant direction) {
+ if (g_map)
+ g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), direction);
+
+ // clone2727 says: Is this necessary?
+ _vm->_gfx->setCurSurface(_navMovie.getSurface());
+ _pushIn.copyToCurrentPort();
+ _vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea());
+
+ // Added 2/10/97. Shouldn't this be here? Shouldn't we set the current activation to
+ // always when turning to a new view?
+ _currentActivation = kActivateHotSpotAlways;
+
+ _interruptionFilter = kFilterAllInput;
+
+ if (direction != GameState.getCurrentDirection()) {
+ GameState.setCurrentDirection(direction);
+ activateCurrentView(GameState.getCurrentRoom(), direction, kSpotOnTurnMask);
+ } else {
+ showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
+ }
+
+ if (GameState.getOpenDoorRoom() != kNoRoomID) {
+ // Turning always closes a door.
+ loadAmbientLoops();
+ closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection());
+ GameState.setOpenDoorLocation(kNoRoomID, kNoDirection);
+ }
+
+ if (g_AIArea)
+ g_AIArea->checkMiddleArea();
+
+ checkContinuePoint(GameState.getCurrentRoom(), direction);
+
+ _vm->_cursor->hideUntilMoved();
+}
+
+void Neighborhood::spotCompleted() {
+ _interruptionFilter = kFilterAllInput;
+ showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
+}
+
+void Neighborhood::doorOpened() {
+ _interruptionFilter = kFilterAllInput;
+
+ // 2/23/97
+ // Fixes funny bug with doors that are opened by dropping things on them...
+ setCurrentActivation(kActivateHotSpotAlways);
+
+ GameState.setOpenDoorLocation(GameState.getCurrentRoom(), GameState.getCurrentDirection());
+
+ SpotTable::Entry entry;
+ findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask, entry);
+
+ if (entry.dstFlags & kSpotOnDoorOpenMask) {
+ startSpotOnceOnly(entry.movieStart, entry.movieEnd);
+ } else {
+ findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask | kSpotLoopsMask, entry);
+
+ if (entry.dstFlags & kSpotOnDoorOpenMask)
+ startSpotLoop(entry.movieStart, entry.movieEnd);
+ }
+
+ loadAmbientLoops();
+
+ if (g_map)
+ g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection());
+
+ if (g_AIArea)
+ g_AIArea->checkMiddleArea();
+}
+
+void Neighborhood::moveForward() {
+ ExitTable::Entry exitEntry;
+ CanMoveForwardReason moveReason = canMoveForward(exitEntry);
+
+ if (moveReason == kCanMoveForward)
+ startExitMovie(exitEntry);
+ else
+ cantMoveThatWay(moveReason);
+}
+
+void Neighborhood::turn(const TurnDirection turnDirection) {
+ DirectionConstant nextDir;
+ CanTurnReason turnReason = canTurn(turnDirection, nextDir);
+
+ if (turnReason == kCanTurn)
+ startTurnPush(turnDirection, getViewTime(GameState.getCurrentRoom(), nextDir), nextDir);
+ else
+ cantTurnThatWay(turnReason);
+}
+
+void Neighborhood::turnLeft() {
+ turn(kTurnLeft);
+}
+
+void Neighborhood::turnRight() {
+ turn(kTurnRight);
+}
+
+void Neighborhood::turnUp() {
+ turn(kTurnUp);
+}
+
+void Neighborhood::turnDown() {
+ turn(kTurnDown);
+}
+
+void Neighborhood::openDoor() {
+ DoorTable::Entry door;
+ CanOpenDoorReason doorReason = canOpenDoor(door);
+
+ if (doorReason == kCanOpenDoor)
+ startDoorOpenMovie(door.movieStart, door.movieEnd);
+ else
+ cantOpenDoor(doorReason);
+}
+
+void Neighborhood::zoomTo(const Hotspot *hotspot) {
+ ZoomTable::Entry zoomEntry;
+ getZoomEntry(hotspot->getObjectID(), zoomEntry);
+ if (!zoomEntry.isEmpty())
+ startZoomMovie(zoomEntry);
+}
+
+void Neighborhood::updateViewFrame() {
+ showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
+}
+
+void Neighborhood::startSpotLoop(TimeValue startTime, TimeValue stopTime, NotificationFlags flags) {
+ _turnPush.hide();
+ startMovieSequence(startTime, stopTime, flags, true, kFilterAllInput);
+}
+
+void Neighborhood::showViewFrame(TimeValue viewTime) {
+ if ((int32)viewTime >= 0) {
+ _turnPush.hide();
+ _navMovie.stop();
+ _navMovie.setFlags(0);
+ _navMovie.setSegment(0, _navMovie.getDuration());
+ _navMovie.setTime(viewTime);
+
+ Common::Rect pushBounds;
+ _turnPush.getBounds(pushBounds);
+
+ _navMovie.moveElementTo(pushBounds.left, pushBounds.top);
+ _navMovie.show();
+ _navMovie.redrawMovieWorld();
+ }
+}
+
+void Neighborhood::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
+ ExtraTable::Entry entry;
+ getExtraEntry(extraID, entry);
+
+ if (entry.movieStart != 0xffffffff)
+ playExtraMovie(entry, flags, interruptionFilter);
+}
+
+bool Neighborhood::startExtraSequenceSync(const ExtraID extraID, const InputBits interruptionFilter) {
+ InputDevice.waitInput(interruptionFilter);
+ return prepareExtraSync(extraID) && waitMovieFinish(&_navMovie, interruptionFilter);
+}
+
+void Neighborhood::loopExtraSequence(const uint32 extraID, NotificationFlags flags) {
+ ExtraTable::Entry entry;
+ getExtraEntry(extraID, entry);
+
+ if (entry.movieStart != 0xffffffff) {
+ _lastExtra = extraID;
+ startSpotLoop(entry.movieStart, entry.movieEnd, flags);
+ }
+}
+
+bool Neighborhood::navMoviePlaying() {
+ return _navMovie.isRunning();
+}
+
+void Neighborhood::playDeathExtra(ExtraID extra, DeathReason deathReason) {
+ _extraDeathReason = deathReason;
+ startExtraSequence(extra, kDeathExtraCompletedFlag, kFilterNoInput);
+}
+
+void Neighborhood::die(const DeathReason deathReason) {
+ loadLoopSound1("");
+ loadLoopSound2("");
+ _vm->die(deathReason);
+}
+
+void Neighborhood::setSoundFXLevel(const uint16 fxLevel) {
+ if (_navMovie.isSurfaceValid())
+ _navMovie.setVolume(fxLevel);
+ if (_spotSounds.isSoundLoaded())
+ _spotSounds.setVolume(fxLevel);
+ if (_currentInteraction)
+ _currentInteraction->setSoundFXLevel(fxLevel);
+}
+
+void Neighborhood::setAmbienceLevel(const uint16 ambientLevel) {
+ if (_soundLoop1.isSoundLoaded())
+ _loop1Fader.setMasterVolume(_vm->getAmbienceLevel());
+ if (_soundLoop2.isSoundLoaded())
+ _loop2Fader.setMasterVolume(_vm->getAmbienceLevel());
+ if (_currentInteraction)
+ _currentInteraction->setAmbienceLevel(ambientLevel);
+}
+
+// Force the exit taken from (room, direction, alternate) to come to a stop.
+void Neighborhood::forceStridingStop(const RoomID room, const DirectionConstant direction, const AlternateID alternate) {
+ ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate);
+
+ if (entry.movieStart != 0xffffffff) {
+ TimeValue strideStop = entry.exitEnd;
+ TimeValue exitStop = entry.movieEnd;
+
+ if (strideStop != exitStop) {
+ for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) {
+ entry = *it;
+
+ if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) {
+ entry.exitEnd = exitStop;
+ *it = entry;
+ }
+ }
+ }
+ }
+}
+
+// Restore the exit taken from (room, direction, alternate) to stride.
+void Neighborhood::restoreStriding(const RoomID room, const DirectionConstant direction, const AlternateID alternate) {
+ ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate);
+
+ if (entry.movieStart != 0xffffffff) {
+ TimeValue strideStop = entry.exitEnd;
+ TimeValue exitStop = entry.movieEnd;
+
+ if (strideStop != entry.originalEnd) {
+ for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) {
+ entry = *it;
+
+ if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) {
+ entry.exitEnd = entry.originalEnd;
+ *it = entry;
+ }
+ }
+ }
+ }
+}
+
+HotspotInfoTable::Entry *Neighborhood::findHotspotEntry(const HotSpotID id) {
+ for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++)
+ if (it->hotspot == id)
+ return &(*it);
+
+ return 0;
+}
+
+void Neighborhood::hideNav() {
+ _isRunning = _navMovie.isRunning();
+ _navMovie.stop();
+ _navMovie.hide();
+ _turnPush.stopFader();
+ _turnPush.hide();
+}
+
+void Neighborhood::showNav() {
+ _navMovie.show();
+ _turnPush.hide();
+ if (_isRunning)
+ _navMovie.start();
+}
+
+void Neighborhood::startExitMovie(const ExitTable::Entry &exitEntry) {
+ FaderMoveSpec compassMove;
+
+ if (g_compass)
+ getExitCompassMove(exitEntry, compassMove);
+
+ GameState.setNextRoom(exitEntry.exitRoom);
+ GameState.setNextDirection(exitEntry.exitDirection);
+
+ if (exitEntry.movieEnd == exitEntry.exitEnd) // Just a walk.
+ startMovieSequence(exitEntry.movieStart, exitEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false);
+ else // We're stridin'!
+ startMovieSequence(exitEntry.movieStart, exitEntry.exitEnd, kStrideCompletedFlag, kFilterNoInput, false, exitEntry.movieEnd);
+
+ if (g_compass)
+ g_compass->startFader(compassMove);
+}
+
+void Neighborhood::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
+ FaderMoveSpec compassMove;
+
+ if (g_compass)
+ getZoomCompassMove(zoomEntry, compassMove);
+
+ GameState.setNextRoom(zoomEntry.room);
+ GameState.setNextDirection(zoomEntry.direction);
+
+ startMovieSequence(zoomEntry.movieStart, zoomEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false);
+
+ if (g_compass)
+ g_compass->startFader(compassMove);
+}
+
+void Neighborhood::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) {
+ startMovieSequence(startTime, stopTime, kDoorOpenCompletedFlag, kFilterNoInput, false);
+}
+
+void Neighborhood::startTurnPush(const TurnDirection turnDirection, const TimeValue newView, const DirectionConstant nextDir) {
+ if (g_AIArea)
+ g_AIArea->lockAIOut();
+
+ _vm->_cursor->hide();
+
+ GameState.setNextDirection(nextDir);
+
+ _interruptionFilter = kFilterNoInput;
+ _turnPush.stopFader();
+
+ // Set up callback.
+ _turnPushCallBack.setCallBackFlag(kTurnCompletedFlag);
+ _turnPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ // Stop nav movie.
+ _navMovie.stop();
+ _navMovie.setFlags(0);
+
+ // Set segment of nav movie to whole movie, so that subsequent initFromMovieFrame
+ // will work.
+ _navMovie.setSegment(0, _navMovie.getDuration());
+
+ _pushIn.initFromMovieFrame(_navMovie.getMovie(), newView);
+
+ _navMovie.hide();
+
+ switch (turnDirection) {
+ case kTurnLeft:
+ _turnPush.setSlideDirection(kSlideRightMask);
+ break;
+ case kTurnRight:
+ _turnPush.setSlideDirection(kSlideLeftMask);
+ break;
+ case kTurnUp:
+ _turnPush.setSlideDirection(kSlideDownMask);
+ break;
+ case kTurnDown:
+ _turnPush.setSlideDirection(kSlideUpMask);
+ break;
+ }
+
+ _turnPush.show();
+
+ FaderMoveSpec moveSpec;
+ moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000);
+ _turnPush.startFader(moveSpec);
+
+ if (g_compass) {
+ _turnPush.pauseFader();
+
+ int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ int32 stopAngle = getStaticCompassAngle(GameState.getCurrentRoom(), nextDir);
+
+ if (turnDirection == kTurnLeft) {
+ if (startAngle < stopAngle)
+ startAngle += 360;
+ } else {
+ if (stopAngle < startAngle)
+ stopAngle += 360;
+ }
+
+ FaderMoveSpec turnSpec;
+ _turnPush.getCurrentFaderMove(turnSpec);
+
+ FaderMoveSpec compassMove;
+ compassMove.makeTwoKnotFaderSpec(turnSpec.getFaderScale(), turnSpec.getNthKnotTime(0), startAngle, turnSpec.getNthKnotTime(1), stopAngle);
+ g_compass->startFader(compassMove);
+ }
+
+ _turnPushCallBack.cancelCallBack();
+ _turnPush.continueFader();
+
+ do {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ } while (_turnPush.isFading());
+
+ _turnPush.stopFader();
+ _neighborhoodNotification.setNotificationFlags(kTurnCompletedFlag, kTurnCompletedFlag);
+}
+
+void Neighborhood::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) {
+ FaderMoveSpec compassMove;
+
+ if (g_compass)
+ getExtraCompassMove(extraEntry, compassMove);
+
+ _lastExtra = extraEntry.extra;
+ _turnPush.hide();
+ startMovieSequence(extraEntry.movieStart, extraEntry.movieEnd, flags, false, interruptionInput);
+
+ if (g_compass)
+ g_compass->startFader(compassMove);
+}
+
+void Neighborhood::activateCurrentView(const RoomID room, const DirectionConstant direction, SpotFlags flag) {
+ SpotTable::Entry entry;
+ findSpotEntry(room, direction, flag, entry);
+
+ if (entry.dstFlags & flag) {
+ startSpotOnceOnly(entry.movieStart, entry.movieEnd);
+ } else {
+ findSpotEntry(room, direction, flag | kSpotLoopsMask, entry);
+
+ if (entry.dstFlags & flag)
+ startSpotLoop(entry.movieStart, entry.movieEnd);
+ else
+ showViewFrame(getViewTime(room, direction));
+ }
+}
+
+void Neighborhood::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) {
+ switch (_vm->getDragType()) {
+ case kDragInventoryUse:
+ if ((hotspot->getHotspotFlags() & kDropItemSpotFlag) != 0 &&
+ _vm->getDraggingItem()->getObjectID() == entry.hotspotItem)
+ hotspot->setActive();
+ break;
+ case kDragInventoryPickup:
+ case kDragBiochipPickup:
+ // Do nothing -- neighborhoods activate no hot spots in this case...
+ break;
+ default:
+ if ((hotspot->getHotspotFlags() & kPickUpBiochipSpotFlag) != 0) {
+ Item *item = _vm->getAllItems().findItemByID(entry.hotspotItem);
+ if (item && item->getItemNeighborhood() == getObjectID())
+ hotspot->setActive();
+ } else {
+ HotSpotFlags flags = hotspot->getHotspotFlags();
+
+ if ((flags & kNeighborhoodSpotFlag) != 0) {
+ if (flags & kOpenDoorSpotFlag) {
+ if (!GameState.isCurrentDoorOpen())
+ hotspot->setActive();
+ } else if ((flags & (kZoomSpotFlags | kClickSpotFlag | kPlayExtraSpotFlag)) != 0) {
+ hotspot->setActive();
+ } else if ((flags & kPickUpItemSpotFlag) != 0) {
+ // Changed this 2/19/96
+ // Should only light up this hot spot if the item's taken flag is not
+ // set. It's not based on neighborhood ID since that can be reset by the
+ // destroying process.
+
+ if (!GameState.isTakenItemID(entry.hotspotItem))
+ hotspot->setActive();
+ }
+ }
+ }
+ break;
+ }
+}
+
+void Neighborhood::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) {
+ _turnPush.hide();
+ startMovieSequence(startTime, stopTime, kSpotCompletedFlag, kFilterNoInput, false);
+}
+
+void Neighborhood::startMovieSequence(const TimeValue startTime, const TimeValue stopTime, NotificationFlags flags, bool loopSequence,
+ const InputBits interruptionInput, const TimeValue strideStop) {
+ if (!loopSequence && g_AIArea)
+ g_AIArea->lockAIOut();
+
+ _interruptionFilter = interruptionInput;
+
+ // Stop the movie before doing anything else
+ _navMovie.stop();
+
+ Common::Rect pushBounds;
+ _turnPush.getBounds(pushBounds);
+
+ _navMovie.moveElementTo(pushBounds.left, pushBounds.top);
+ _navMovie.show();
+ _navMovie.setFlags(0);
+ _navMovie.setSegment(startTime, stopTime);
+ _navMovie.setTime(startTime);
+
+ if (loopSequence)
+ _navMovie.setFlags(kLoopTimeBase);
+ else
+ flags |= kNeighborhoodMovieCompletedFlag;
+
+ if (strideStop != 0xffffffff)
+ // Subtract a little slop from the striding stop time to keep from "pumping" at the
+ // end of a walk.
+ // 40 is one frame (scale == 600, 15 fps).
+ scheduleStridingCallBack(strideStop - kStridingSlop, flags);
+ else
+ scheduleNavCallBack(flags);
+
+ _navMovie.start();
+}
+
+void Neighborhood::throwAwayInterface() {
+ _doorTable.clear();
+ _exitTable.clear();
+ _extraTable.clear();
+ _hotspotInfoTable.clear();
+ _spotTable.clear();
+ _turnTable.clear();
+ _viewTable.clear();
+ _zoomTable.clear();
+
+ _navMovie.stopDisplaying();
+ _navMovie.releaseMovie();
+ _pushIn.deallocateSurface();
+ _turnPush.stopDisplaying();
+ _turnPush.setInAndOutElements(0, 0);
+ _turnPush.disposeAllCallBacks();
+
+ for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++)
+ _vm->getAllHotspots().remove(*it);
+
+ _neighborhoodHotspots.deleteHotspots();
+ _spotSounds.disposeSound();
+ _delayTimer.disposeAllCallBacks();
+
+ if (g_AIArea) {
+ g_AIArea->saveAIState();
+ g_AIArea->removeAllRules();
+ }
+
+ if (_currentInteraction)
+ newInteraction(kNoInteractionID);
+
+ _croppedMovie.releaseMovie();
+
+ loadLoopSound1("");
+ loadLoopSound2("");
+
+ if (g_energyMonitor) {
+ g_energyMonitor->stopEnergyDraining();
+ g_energyMonitor->saveCurrentEnergyValue();
+ }
+
+ delete g_interface;
+}
+
+bool Neighborhood::prepareExtraSync(const ExtraID extraID) {
+ ExtraTable::Entry extraEntry;
+ FaderMoveSpec compassMove;
+
+ if (g_compass) {
+ getExtraEntry(extraID, extraEntry);
+ getExtraCompassMove(extraEntry, compassMove);
+ }
+
+ ExtraTable::Entry entry;
+ getExtraEntry(extraID, entry);
+ bool result;
+
+ if (entry.movieStart != 0xffffffff) {
+ _turnPush.hide();
+
+ // Stop the movie before doing anything else
+ _navMovie.stop();
+
+ Common::Rect pushBounds;
+ _turnPush.getBounds(pushBounds);
+ _navMovie.moveElementTo(pushBounds.left, pushBounds.top);
+
+ _navMovie.show();
+ _navMovie.setFlags(0);
+ _navMovie.setSegment(entry.movieStart, entry.movieEnd);
+ _navMovie.setTime(entry.movieStart);
+ _navMovie.start();
+ result = true;
+ } else {
+ result = false;
+ }
+
+ if (result && g_compass)
+ g_compass->startFader(compassMove);
+
+ return result;
+}
+
+bool Neighborhood::waitMovieFinish(Movie *movie, const InputBits interruptionFilter) {
+ Input input;
+ bool result = true;
+ bool saveAllowed = _vm->swapSaveAllowed(false);
+ bool openAllowed = _vm->swapLoadAllowed(false);
+
+ while (movie->isRunning()) {
+ InputDevice.getInput(input, interruptionFilter);
+
+ if (input.anyInput() || _vm->shouldQuit()) {
+ result = false;
+ break;
+ }
+
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+
+ movie->stop();
+ _vm->swapSaveAllowed(saveAllowed);
+ _vm->swapLoadAllowed(openAllowed);
+
+ return result;
+}
+
+InputBits Neighborhood::getInputFilter() {
+ return _interruptionFilter & InputHandler::getInputFilter();
+}
+
+void Neighborhood::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) {
+ int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ int32 stopAngle = getStaticCompassAngle(zoomEntry.room, zoomEntry.direction);
+
+ if (startAngle > stopAngle) {
+ if (stopAngle + 180 < startAngle)
+ stopAngle += 360;
+ } else {
+ if (startAngle + 180 < stopAngle)
+ startAngle += 360;
+ }
+
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), zoomEntry.movieStart, startAngle, zoomEntry.movieEnd, stopAngle);
+}
+
+void Neighborhood::getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &compassMove) {
+ compassMove.makeOneKnotFaderSpec(g_compass->getFaderValue());
+}
+
+void Neighborhood::setUpAIRules() {
+ // Set up default rules here:
+ // -- Energy warning rules.
+
+ if (g_AIArea) {
+ g_AIArea->forceAIUnlocked();
+
+ if (!_vm->isDemo() && (getObjectID() == kPrehistoricID || getObjectID() == kNoradAlphaID ||
+ getObjectID() == kNoradDeltaID || getObjectID() == kMarsID || getObjectID() == kWSCID)) {
+
+ AIEnergyMonitorCondition *condition50 = new AIEnergyMonitorCondition(kWorriedEnergy);
+ AIPlayMessageAction *message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4A", false);
+ AIRule *rule50 = new AIRule(condition50, message);
+
+ AIEnergyMonitorCondition *condition25 = new AIEnergyMonitorCondition(kNervousEnergy);
+ AICompoundAction *compound = new AICompoundAction();
+ message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4B", false);
+ compound->addAction(message);
+ AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule50);
+ compound->addAction(deactivate);
+ AIRule *rule25 = new AIRule(condition25, compound);
+
+ AIEnergyMonitorCondition *condition5 = new AIEnergyMonitorCondition(kPanicStrickenEnergy);
+ compound = new AICompoundAction();
+ message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4C", false);
+ compound->addAction(message);
+ deactivate = new AIDeactivateRuleAction(rule50);
+ compound->addAction(deactivate);
+ deactivate = new AIDeactivateRuleAction(rule25);
+ compound->addAction(deactivate);
+ AIRule *rule5 = new AIRule(condition5, compound);
+
+ g_AIArea->addAIRule(rule5);
+ g_AIArea->addAIRule(rule25);
+ g_AIArea->addAIRule(rule50);
+ }
+ }
+}
+
+GameInteraction *Neighborhood::makeInteraction(const InteractionID interactionID) {
+ if (interactionID == kNoInteractionID)
+ return 0;
+
+ return new GameInteraction(interactionID, this);
+}
+
+void Neighborhood::newInteraction(const InteractionID interactionID) {
+ GameInteraction *interaction = makeInteraction(interactionID);
+ _doneWithInteraction = false;
+
+ if (_currentInteraction) {
+ _currentInteraction->stopInteraction();
+ delete _currentInteraction;
+ }
+
+ _currentInteraction = interaction;
+
+ if (_currentInteraction)
+ _currentInteraction->startInteraction();
+
+ if (g_AIArea)
+ g_AIArea->checkMiddleArea();
+}
+
+void Neighborhood::bumpIntoWall() {
+ _vm->_gfx->shakeTheWorld(15, 30);
+}
+
+void Neighborhood::zoomUpOrBump() {
+ Hotspot *zoomSpot = 0;
+
+ for (HotspotList::iterator it = _vm->getAllHotspots().begin(); it != _vm->getAllHotspots().end(); it++) {
+ Hotspot *hotspot = *it;
+
+ if ((hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomInSpotFlag)) == (kNeighborhoodSpotFlag | kZoomInSpotFlag)) {
+ HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID());
+
+ if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) {
+ if (zoomSpot) {
+ zoomSpot = 0;
+ break;
+ } else {
+ zoomSpot = hotspot;
+ }
+ }
+ }
+ }
+
+ if (zoomSpot)
+ zoomTo(zoomSpot);
+ else
+ bumpIntoWall();
+}
+
+void Neighborhood::loadLoopSound1(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) {
+ FaderMoveSpec faderMove;
+
+ if (!loop1Loaded(soundName)) {
+ _loop1SoundString = soundName;
+
+ if (_soundLoop1.isSoundLoaded()) {
+ faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeOut, 0);
+ _loop1Fader.startFaderSync(faderMove);
+ }
+
+ if (!_loop1SoundString.empty()) {
+ _soundLoop1.initFromAIFFFile(_loop1SoundString);
+ _soundLoop1.loopSound();
+ _loop1Fader.setMasterVolume(_vm->getAmbienceLevel());
+ _loop1Fader.setFaderValue(0);
+ faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume);
+ _loop1Fader.startFaderSync(faderMove);
+ } else {
+ _soundLoop1.disposeSound();
+ }
+ } else if (_loop1Fader.getFaderValue() != volume) {
+ faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeIn, volume);
+ _loop1Fader.startFaderSync(faderMove);
+ }
+}
+
+void Neighborhood::loadLoopSound2(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) {
+ FaderMoveSpec faderMove;
+
+ if (!loop2Loaded(soundName)) {
+ _loop2SoundString = soundName;
+
+ if (_soundLoop2.isSoundLoaded()) {
+ faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeOut, 0);
+ _loop2Fader.startFaderSync(faderMove);
+ }
+
+ if (!_loop2SoundString.empty()) {
+ _soundLoop2.initFromAIFFFile(_loop2SoundString);
+ _soundLoop2.loopSound();
+ _loop2Fader.setMasterVolume(_vm->getAmbienceLevel());
+ _loop2Fader.setFaderValue(0);
+ faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume);
+ _loop2Fader.startFaderSync(faderMove);
+ } else {
+ _soundLoop2.disposeSound();
+ }
+ } else if (_loop2Fader.getFaderValue() != volume) {
+ faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeIn, volume);
+ _loop2Fader.startFaderSync(faderMove);
+ }
+}
+
+void Neighborhood::takeItemFromRoom(Item *item) {
+ item->setItemRoom(kNoNeighborhoodID, kNoRoomID, kNoDirection);
+ // Also set the taken item flag. Do this before updating the view frame.
+ GameState.setTakenItem(item, true);
+ updateViewFrame();
+}
+
+void Neighborhood::dropItemIntoRoom(Item *item, Hotspot *) {
+ item->setItemRoom(getObjectID(), GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ // Also set the taken item flag. Do this before updating the view frame.
+ GameState.setTakenItem(item, false);
+ updateViewFrame();
+}
+
+void Neighborhood::makeContinuePoint() {
+ _vm->makeContinuePoint();
+}
+
+void Neighborhood::startLoop1Fader(const FaderMoveSpec &faderMove) {
+ _loop1Fader.startFader(faderMove);
+}
+
+void Neighborhood::startLoop2Fader(const FaderMoveSpec &faderMove) {
+ _loop2Fader.startFader(faderMove);
+}
+
+// *** Revised 6/13/96 to use the last frame of the extra sequence.
+// Necessary for Cinepak buildup.
+void Neighborhood::showExtraView(uint32 extraID) {
+ ExtraTable::Entry entry;
+ getExtraEntry(extraID, entry);
+
+ if (entry.movieEnd != 0xffffffff)
+ showViewFrame(entry.movieEnd - 1);
+}
+
+void Neighborhood::startExtraLongSequence(const uint32 firstExtra, const uint32 lastExtra, NotificationFlags flags,
+ const InputBits interruptionFilter) {
+ ExtraTable::Entry firstEntry, lastEntry;
+ getExtraEntry(firstExtra, firstEntry);
+
+ if (firstEntry.movieStart != 0xffffffff) {
+ getExtraEntry(lastExtra, lastEntry);
+ _lastExtra = firstExtra;
+ _turnPush.hide();
+ startMovieSequence(firstEntry.movieStart, lastEntry.movieEnd, flags, kFilterNoInput, interruptionFilter);
+ }
+}
+
+void Neighborhood::openCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) {
+ if (_croppedMovie.isMovieValid())
+ closeCroppedMovie();
+
+ _croppedMovie.initFromMovieFile(movieName);
+ _croppedMovie.moveElementTo(left, top);
+ _croppedMovie.startDisplaying();
+ _croppedMovie.show();
+}
+
+void Neighborhood::loopCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) {
+ openCroppedMovie(movieName, left, top);
+ _croppedMovie.redrawMovieWorld();
+ _croppedMovie.setFlags(kLoopTimeBase);
+ _croppedMovie.start();
+}
+
+void Neighborhood::closeCroppedMovie() {
+ _croppedMovie.releaseMovie();
+}
+
+void Neighborhood::playCroppedMovieOnce(const Common::String &movieName, CoordType left, CoordType top, const InputBits interruptionFilter) {
+ openCroppedMovie(movieName, left, top);
+ _croppedMovie.redrawMovieWorld();
+ _croppedMovie.start();
+
+ InputBits oldInterruptionFilter = _interruptionFilter;
+ if (oldInterruptionFilter != kFilterNoInput)
+ _interruptionFilter = kFilterNoInput;
+
+ bool saveAllowed = _vm->swapSaveAllowed(false);
+ bool openAllowed = _vm->swapLoadAllowed(false);
+
+ Input input;
+ while (_croppedMovie.isRunning() && !_vm->shouldQuit()) {
+ _vm->processShell();
+ InputDevice.getInput(input, interruptionFilter);
+ if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested() || _vm->shouldQuit())
+ break;
+ _vm->_system->delayMillis(10);
+ }
+
+ if (oldInterruptionFilter != kFilterNoInput)
+ _interruptionFilter = oldInterruptionFilter;
+
+ closeCroppedMovie();
+ _vm->swapSaveAllowed(saveAllowed);
+ _vm->swapLoadAllowed(openAllowed);
+}
+
+void Neighborhood::playMovieSegment(Movie *movie, TimeValue startTime, TimeValue stopTime) {
+ TimeValue oldStart, oldStop;
+ movie->getSegment(oldStart, oldStop);
+
+ if (stopTime == 0xffffffff)
+ stopTime = movie->getDuration();
+
+ movie->setSegment(startTime, stopTime);
+ movie->setTime(startTime);
+ movie->start();
+
+ while (movie->isRunning()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+
+ movie->stop();
+ movie->setSegment(oldStart, oldStop);
+}
+
+void Neighborhood::recallToTSASuccess() {
+ if (GameState.allTimeZonesFinished())
+ _vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth);
+ else
+ _vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth);
+}
+
+void Neighborhood::recallToTSAFailure() {
+ _vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth);
+}
+
+void Neighborhood::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (_vm->getGameMode() == kModeNavigation) {
+ if (input.upButtonAnyDown())
+ upButton(input);
+ else if (input.downButtonAnyDown())
+ downButton(input);
+ else if (input.leftButtonAnyDown())
+ leftButton(input);
+ else if (input.rightButtonAnyDown())
+ rightButton(input);
+ }
+
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+void Neighborhood::setHotspotFlags(const HotSpotID id, const HotSpotFlags flags) {
+ Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(id);
+ hotspot->setMaskedHotspotFlags(flags, flags);
+}
+
+void Neighborhood::setIsItemTaken(const ItemID id) {
+ GameState.setTakenItemID(id, _vm->playerHasItemID(id));
+}
+
+void Neighborhood::upButton(const Input &) {
+ moveForward();
+}
+
+void Neighborhood::leftButton(const Input &) {
+ turnLeft();
+}
+
+void Neighborhood::rightButton(const Input &) {
+ turnRight();
+}
+
+void Neighborhood::downButton(const Input &) {
+ if (_inputHandler->wantsCursor()) {
+ _vm->getAllHotspots().deactivateAllHotspots();
+ _inputHandler->activateHotspots();
+
+ for (HotspotList::iterator it = _vm->getAllHotspots().begin(); it != _vm->getAllHotspots().end(); it++) {
+ Hotspot *hotspot = *it;
+
+ if (hotspot->isSpotActive() && (hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) == (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) {
+ HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID());
+
+ if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) {
+ Input scratch;
+ _inputHandler->clickInHotspot(scratch, hotspot);
+ return;
+ }
+ }
+ }
+ }
+}
+
+void Neighborhood::initOnePicture(Picture *picture, const Common::String &pictureName, DisplayOrder order, CoordType left, CoordType top, bool show) {
+ picture->initFromPICTFile(pictureName);
+ picture->setDisplayOrder(order);
+ picture->moveElementTo(left, top);
+ picture->startDisplaying();
+ if (show)
+ picture->show();
+}
+
+void Neighborhood::initOneMovie(Movie *movie, const Common::String &movieName, DisplayOrder order, CoordType left, CoordType top, bool show) {
+ movie->initFromMovieFile(movieName);
+ movie->setDisplayOrder(order);
+ movie->moveElementTo(left, top);
+ movie->startDisplaying();
+
+ if (show)
+ movie->show();
+
+ movie->redrawMovieWorld();
+}
+
+void Neighborhood::reinstateMonocleInterface() {
+ _vm->_gfx->disableErase();
+
+ _vm->createInterface();
+
+ if (g_AIArea)
+ setNextHandler(g_AIArea);
+
+ init();
+
+ moveNavTo(kNavAreaLeft, kNavAreaTop);
+
+ if (g_interface)
+ g_interface->setDate(getDateResID());
+
+ if (g_AIArea)
+ g_AIArea->restoreAIState();
+}
+
+void Neighborhood::useIdleTime() {
+ if (_doneWithInteraction) {
+ newInteraction(kNoInteractionID);
+ loadAmbientLoops();
+ }
+}
+
+void Neighborhood::timerFunction() {
+ timerExpired(getTimerEvent());
+}
+
+void Neighborhood::scheduleEvent(const TimeValue time, const TimeScale scale, const uint32 eventType) {
+ _eventTimer.stopFuse();
+ _eventTimer.primeFuse(time, scale);
+ _timerEvent = eventType;
+ _eventTimer.setFunctor(new Common::Functor0Mem<void, Neighborhood>(this, &Neighborhood::timerFunction));
+ _eventTimer.lightFuse();
+}
+
+void Neighborhood::cancelEvent() {
+ _eventTimer.stopFuse();
+}
+
+void Neighborhood::pauseTimer() {
+ _eventTimer.pauseFuse();
+}
+
+void Neighborhood::resumeTimer() {
+ // NOTE: Yes, this function calls pauseFuse!
+ // Looks like an original game bug, will need
+ // to investigate how this affects gameplay.
+ _eventTimer.pauseFuse();
+}
+
+bool Neighborhood::timerPaused() {
+ return _eventTimer.isFusePaused();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/neighborhood.h b/engines/pegasus/neighborhood/neighborhood.h
new file mode 100644
index 0000000000..3c1c5eac92
--- /dev/null
+++ b/engines/pegasus/neighborhood/neighborhood.h
@@ -0,0 +1,408 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_H
+#define PEGASUS_NEIGHBORHOOD_H
+
+#include "common/queue.h"
+#include "common/str.h"
+
+#include "pegasus/fader.h"
+#include "pegasus/hotspot.h"
+#include "pegasus/input.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+#include "pegasus/sound.h"
+#include "pegasus/timers.h"
+#include "pegasus/transition.h"
+#include "pegasus/util.h"
+#include "pegasus/neighborhood/door.h"
+#include "pegasus/neighborhood/exit.h"
+#include "pegasus/neighborhood/extra.h"
+#include "pegasus/neighborhood/hotspotinfo.h"
+#include "pegasus/neighborhood/spot.h"
+#include "pegasus/neighborhood/turn.h"
+#include "pegasus/neighborhood/view.h"
+#include "pegasus/neighborhood/zoom.h"
+
+namespace Pegasus {
+
+class PegasusEngine;
+
+// Pegasus Prime neighborhood id's
+static const NeighborhoodID kCaldoriaID = 0;
+static const NeighborhoodID kFullTSAID = 1;
+static const NeighborhoodID kFinalTSAID = 2;
+static const NeighborhoodID kTinyTSAID = 3;
+static const NeighborhoodID kPrehistoricID = 4;
+static const NeighborhoodID kMarsID = 5;
+static const NeighborhoodID kWSCID = 6;
+static const NeighborhoodID kNoradAlphaID = 7;
+static const NeighborhoodID kNoradDeltaID = 8;
+// The sub chase is not really a neighborhood, but we define a constant that is used
+// to allow an easy transition out of Norad Alpha.
+static const NeighborhoodID kNoradSubChaseID = 1000;
+
+static const TimeScale kDefaultLoopFadeScale = kThirtyTicksPerSecond;
+static const TimeValue kDefaultLoopFadeOut = kHalfSecondPerThirtyTicks;
+static const TimeValue kDefaultLoopFadeIn = kHalfSecondPerThirtyTicks;
+
+enum QueueRequestType {
+ kNavExtraRequest,
+ kSpotSoundRequest,
+ kDelayRequest
+};
+
+// For delay requests, start is interpreted as the total delay and stop is interpreted
+// as the scale the delay is in.
+// For extra requests, start and stop are not used.
+struct QueueRequest {
+ QueueRequestType requestType;
+ ExtraID extra;
+ TimeValue start, stop;
+ InputBits interruptionFilter;
+ bool playing;
+ NotificationFlags flags;
+ Notification *notification;
+};
+
+bool operator==(const QueueRequest &arg1, const QueueRequest &arg2);
+bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2);
+
+class GameInteraction;
+class Item;
+class Neighborhood;
+
+class StriderCallBack : public TimeBaseCallBack {
+public:
+ StriderCallBack(Neighborhood *);
+ virtual ~StriderCallBack() {}
+
+protected:
+ virtual void callBack();
+
+ Neighborhood *_neighborhood;
+};
+
+typedef Common::Queue<QueueRequest> NeighborhoodActionQueue;
+
+class Neighborhood : public IDObject, public NotificationReceiver, public InputHandler, public Idler {
+friend class StriderCallBack;
+
+public:
+ Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id);
+ virtual ~Neighborhood();
+
+ virtual void init();
+ virtual void start();
+ virtual void moveNavTo(const CoordType, const CoordType);
+ virtual void checkContinuePoint(const RoomID, const DirectionConstant) = 0;
+ void makeContinuePoint();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual CanMoveForwardReason canMoveForward(ExitTable::Entry &entry);
+ virtual CanTurnReason canTurn(TurnDirection turn, DirectionConstant &nextDir);
+ virtual CanOpenDoorReason canOpenDoor(DoorTable::Entry &entry);
+
+ virtual void cantMoveThatWay(CanMoveForwardReason);
+ virtual void cantTurnThatWay(CanTurnReason) {}
+ virtual void cantOpenDoor(CanOpenDoorReason);
+ virtual void arriveAt(const RoomID room, const DirectionConstant direction);
+ virtual void turnTo(const DirectionConstant);
+ virtual void spotCompleted();
+ virtual void doorOpened();
+ virtual void closeDoorOffScreen(const RoomID, const DirectionConstant) {}
+
+ virtual void moveForward();
+ virtual void turn(const TurnDirection);
+ virtual void turnLeft();
+ virtual void turnRight();
+ virtual void turnUp();
+ virtual void turnDown();
+ virtual void openDoor();
+ virtual void zoomTo(const Hotspot *);
+
+ virtual void updateViewFrame();
+
+ void requestExtraSequence(const ExtraID, const NotificationFlags, const InputBits interruptionFilter);
+ void requestSpotSound(const TimeValue, const TimeValue, const InputBits interruptionFilter, const NotificationFlags);
+ void playSpotSoundSync(const TimeValue in, const TimeValue out);
+ void requestDelay(const TimeValue, const TimeScale, const InputBits interruptionFilter, const NotificationFlags);
+
+ Notification *getNeighborhoodNotification() { return &_neighborhoodNotification; }
+
+ virtual void getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry);
+ virtual void startSpotLoop(TimeValue, TimeValue, NotificationFlags = 0);
+ virtual bool actionQueueEmpty() { return _actionQueue.empty(); }
+ virtual void showViewFrame(TimeValue);
+ virtual void findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry);
+ virtual void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits interruptionFilter);
+ bool startExtraSequenceSync(const ExtraID, const InputBits);
+ virtual void loopExtraSequence(const uint32, NotificationFlags = 0);
+ int32 getLastExtra() const { return _lastExtra; }
+ virtual void scheduleNavCallBack(NotificationFlags);
+
+ Movie *getNavMovie() { return &_navMovie; }
+ bool navMoviePlaying();
+
+ void setCurrentAlternate(const AlternateID alt) { _currentAlternate = alt; }
+ AlternateID getCurrentAlternate() const { return _currentAlternate; }
+
+ void setCurrentActivation(const HotSpotActivationID a) { _currentActivation = a; }
+ HotSpotActivationID getCurrentActivation() { return _currentActivation; }
+
+ virtual void playDeathExtra(ExtraID, DeathReason);
+ virtual void die(const DeathReason);
+
+ virtual void setSoundFXLevel(const uint16);
+ virtual void setAmbienceLevel(const uint16);
+
+ void forceStridingStop(const RoomID, const DirectionConstant, const AlternateID);
+ void restoreStriding(const RoomID, const DirectionConstant, const AlternateID);
+
+ HotspotInfoTable::Entry *findHotspotEntry(const HotSpotID);
+
+ Push *getTurnPush() { return &_turnPush; }
+ Picture *getTurnPushPicture() { return &_pushIn; }
+
+ void hideNav();
+ void showNav();
+
+ virtual void loadAmbientLoops() {}
+
+ virtual void flushGameState() {}
+
+ virtual Common::String getBriefingMovie();
+ virtual Common::String getEnvScanMovie();
+ virtual uint getNumHints();
+ virtual Common::String getHintMovie(uint);
+ virtual bool canSolve();
+ virtual void prepareForAIHint(const Common::String &) {}
+ virtual void cleanUpAfterAIHint(const Common::String &) {}
+ virtual void doSolve();
+
+ virtual bool okayToJump();
+
+ virtual AirQuality getAirQuality(const RoomID);
+ virtual void checkAirMask() {}
+ virtual void checkFlashlight() {}
+ virtual void shieldOn() {}
+ virtual void shieldOff() {}
+
+ virtual void loadLoopSound1(const Common::String &, const uint16 volume = 0x100,
+ const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn,
+ const TimeScale fadeScale = kDefaultLoopFadeScale);
+ virtual void loadLoopSound2(const Common::String &, const uint16 volume = 0x100,
+ const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn,
+ const TimeScale fadeScale = kDefaultLoopFadeScale);
+ bool loop1Loaded(const Common::String &soundName) { return _loop1SoundString == soundName; }
+ bool loop2Loaded(const Common::String &soundName) { return _loop2SoundString == soundName; }
+ void startLoop1Fader(const FaderMoveSpec &);
+ void startLoop2Fader(const FaderMoveSpec &);
+
+ virtual void takeItemFromRoom(Item *);
+ virtual void dropItemIntoRoom(Item *, Hotspot *);
+ virtual Hotspot *getItemScreenSpot(Item *, DisplayElement *) { return 0; }
+
+ virtual GameInteraction *makeInteraction(const InteractionID);
+ virtual void requestDeleteCurrentInteraction() { _doneWithInteraction = true; }
+
+ virtual uint16 getDateResID() const = 0;
+
+ virtual void showExtraView(uint32);
+ virtual void startExtraLongSequence(const uint32, const uint32, NotificationFlags, const InputBits interruptionFilter);
+
+ void openCroppedMovie(const Common::String &, CoordType, CoordType);
+ void loopCroppedMovie(const Common::String &, CoordType, CoordType);
+ void closeCroppedMovie();
+ void playCroppedMovieOnce(const Common::String &, CoordType, CoordType, const InputBits interruptionFilter = kFilterNoInput);
+
+ void playMovieSegment(Movie *, TimeValue = 0, TimeValue = 0xffffffff);
+
+ virtual void recallToTSASuccess();
+ virtual void recallToTSAFailure();
+
+ virtual void pickedUpItem(Item *) {}
+
+ virtual void handleInput(const Input &, const Hotspot *);
+protected:
+ PegasusEngine *_vm;
+ Common::String _resName;
+
+ virtual Common::String getSoundSpotsName() = 0;
+ virtual Common::String getNavMovieName() = 0;
+
+ // Notification function.
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+
+ // Map info functions.
+ virtual void getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry);
+ virtual TimeValue getViewTime(const RoomID room, const DirectionConstant direction);
+ virtual void getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry);
+ virtual DirectionConstant getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turn);
+ virtual void getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry);
+ virtual void getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry);
+
+ // Nav movie sequences.
+ virtual void startExitMovie(const ExitTable::Entry &);
+ virtual void keepStriding(ExitTable::Entry &);
+ virtual void stopStriding();
+ virtual void checkStriding();
+ virtual bool stillMoveForward();
+ virtual void scheduleStridingCallBack(const TimeValue, NotificationFlags flags);
+ virtual void startZoomMovie(const ZoomTable::Entry &);
+ virtual void startDoorOpenMovie(const TimeValue, const TimeValue);
+ virtual void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant);
+ virtual void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionFilter);
+
+ virtual void activateCurrentView(const RoomID, const DirectionConstant, SpotFlags);
+
+ virtual void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *);
+
+ virtual void startSpotOnceOnly(TimeValue, TimeValue);
+
+ virtual void startMovieSequence(const TimeValue, const TimeValue, NotificationFlags,
+ bool loopSequence, const InputBits interruptionFilter, const TimeValue strideStop = 0xffffffff);
+
+ virtual void createNeighborhoodSpots();
+
+ void resetLastExtra() { _lastExtra = -1; }
+
+ virtual void throwAwayInterface();
+
+ // Action queue stuff
+ void popActionQueue();
+ void serviceActionQueue();
+ void requestAction(const QueueRequestType, const ExtraID, const TimeValue, const TimeValue, const InputBits, const NotificationFlags);
+
+ virtual bool prepareExtraSync(const ExtraID);
+ virtual bool waitMovieFinish(Movie *, const InputBits);
+
+ virtual InputBits getInputFilter();
+
+ // Misc.
+ virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant dir);
+ virtual void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &);
+ virtual void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec&);
+ virtual void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec&);
+
+ virtual void setUpAIRules();
+ virtual void setHotspotFlags(const HotSpotID, const HotSpotFlags);
+ virtual void setIsItemTaken(const ItemID);
+
+ virtual void upButton(const Input &);
+ virtual void leftButton(const Input &);
+ virtual void rightButton(const Input &);
+ virtual void downButton(const Input &);
+
+ void initOnePicture(Picture *, const Common::String &, DisplayOrder, CoordType, CoordType, bool);
+ void initOneMovie(Movie *, const Common::String &, DisplayOrder, CoordType, CoordType, bool);
+
+ void reinstateMonocleInterface();
+
+ virtual void newInteraction(const InteractionID);
+ virtual void useIdleTime();
+ virtual void bumpIntoWall();
+ virtual void zoomUpOrBump();
+
+ void scheduleEvent(const TimeValue, const TimeScale, const uint32);
+ void cancelEvent();
+ virtual void timerExpired(const uint32) {}
+ bool isEventTimerRunning() { return _eventTimer.isFuseLit(); }
+ uint32 getTimerEvent() { return _timerEvent; }
+ void timerFunction();
+
+ void pauseTimer();
+ void resumeTimer();
+ bool timerPaused();
+
+ // Navigation Data
+ DoorTable _doorTable;
+ ExitTable _exitTable;
+ ExtraTable _extraTable;
+ HotspotInfoTable _hotspotInfoTable;
+ SpotTable _spotTable;
+ TurnTable _turnTable;
+ ViewTable _viewTable;
+ ZoomTable _zoomTable;
+ AlternateID _currentAlternate;
+ HotSpotActivationID _currentActivation;
+
+ int32 _lastExtra;
+ DeathReason _extraDeathReason;
+
+ // Graphics
+ Movie _navMovie;
+ Picture _pushIn;
+ Push _turnPush;
+
+ // Callbacks
+ Notification _neighborhoodNotification;
+ NotificationCallBack _navMovieCallBack;
+ StriderCallBack _stridingCallBack;
+ NotificationCallBack _turnPushCallBack;
+ NotificationCallBack _spotSoundCallBack;
+ NotificationCallBack _delayCallBack;
+
+ // Hotspots
+ HotspotList _neighborhoodHotspots;
+
+ // Sounds
+ SoundTimeBase _spotSounds;
+
+ // Action queue
+ NeighborhoodActionQueue _actionQueue;
+ TimeBase _delayTimer;
+
+ // Interruptibility...
+ InputBits _interruptionFilter;
+
+ // Nav hiding (for info support...)
+ bool _isRunning;
+
+ GameInteraction *_currentInteraction;
+ bool _doneWithInteraction;
+ Movie _croppedMovie;
+
+ Sound _soundLoop1;
+ Common::String _loop1SoundString;
+ SoundFader _loop1Fader;
+
+ Sound _soundLoop2;
+ Common::String _loop2SoundString;
+ SoundFader _loop2Fader;
+
+ // The event timer...
+ FuseFunction _eventTimer;
+ uint32 _timerEvent;
+};
+
+extern Neighborhood *g_neighborhood;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp
new file mode 100644
index 0000000000..e2a0267231
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp
@@ -0,0 +1,219 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h"
+
+namespace Pegasus {
+
+static const NotificationFlags kECRSection1FinishedFlag = 1;
+static const NotificationFlags kECRPanFinishedFlag = kECRSection1FinishedFlag << 1;
+static const NotificationFlags kECRSection2FinishedFlag = kECRPanFinishedFlag << 1;
+static const NotificationFlags kECRNotificationFlags = kECRSection1FinishedFlag |
+ kECRPanFinishedFlag |
+ kECRSection2FinishedFlag;
+
+static const TimeValue kSection1Start = 0;
+static const TimeValue kSection1Stop = 25;
+static const TimeValue kPanStart = 0;
+static const TimeValue kPanStop = 20;
+static const TimeValue kSection2Start = 26;
+static const TimeValue kSection2Stop = 1000;
+
+// Seems to be a good value for a 20 second pan.
+static const CoordType kPanPixelsPerFrame = 8;
+
+// Interesting times are in seconds.
+static const TimeValue s_ECRInterestingTimes[] = {
+ 0, 1, 2, 10, 25, 26, 56, 64, 72, 80, 88, 94, 102, 108, 116, 999
+};
+
+// Index into s_ECRInterestingTimes of interesting time before security pan.
+static const int kBeforePanTime = 3;
+
+// Index into s_ECRInterestingTimes of interesting time after security pan.
+static const int kAfterPanTime = 5;
+
+NoradAlphaECRMonitor::NoradAlphaECRMonitor(Neighborhood *nextHandler) : GameInteraction(kNoradECRMonitorInteractionID, nextHandler),
+ _ecrSlideShowNotification(kNoradECRNotificationID, (PegasusEngine *)g_engine), _ecrMovie(kECRSlideShowMovieID),
+ _ecrPan(kECRPanID) {
+}
+
+void NoradAlphaECRMonitor::receiveNotification(Notification *, const NotificationFlags flags) {
+ if (flags & kECRSection1FinishedFlag)
+ ecrSection1Finished();
+ else if (flags & kECRPanFinishedFlag)
+ ecrPanFinished();
+ else if (flags & kECRSection2FinishedFlag)
+ ecrSection2Finished();
+}
+
+int NoradAlphaECRMonitor::findCurrentInterestingTime() {
+ TimeValue time = _ecrMovie.getTime();
+ TimeScale scale = _ecrMovie.getScale();
+
+ for (int i = ARRAYSIZE(s_ECRInterestingTimes) - 1; i >= 0; i--)
+ if (time >= s_ECRInterestingTimes[i] * scale)
+ return i;
+
+ return 0;
+}
+
+void NoradAlphaECRMonitor::skipToNextInterestingTime() {
+ if (_ecrMovie.isRunning()) {
+ int interestingTime = findCurrentInterestingTime();
+ _ecrMovie.setTime(s_ECRInterestingTimes[interestingTime + 1] * _ecrMovie.getScale());
+ _ecrMovie.redrawMovieWorld();
+ } else if (_ecrPan.isRunning()) {
+ _ecrPanCallBack.cancelCallBack();
+ ecrPanFinished();
+ }
+}
+
+void NoradAlphaECRMonitor::skipToPreviousInterestingTime() {
+ if (_ecrPan.isRunning()) {
+ _ecrPan.stop();
+ _ecrPan.stopDisplaying();
+ _ecrPanCallBack.cancelCallBack();
+
+ _ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag);
+ _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ TimeScale scale = _ecrMovie.getScale();
+ _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1);
+ _ecrMovie.setTime(s_ECRInterestingTimes[kBeforePanTime] * scale);
+ _ecrMovie.start();
+ } else {
+ int interestingTime = findCurrentInterestingTime();
+
+ if (interestingTime == kAfterPanTime) {
+ _ecrMovieCallBack.cancelCallBack();
+ TimeScale scale = _ecrMovie.getScale();
+ _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1);
+ _ecrMovie.setTime(kSection1Stop * scale);
+ ecrSection1Finished();
+ } else if (interestingTime == 0) {
+ _ecrMovie.setTime(kSection1Start * _ecrMovie.getScale());
+ _ecrMovie.redrawMovieWorld();
+ } else {
+ _ecrMovie.setTime(s_ECRInterestingTimes[interestingTime - 1] * _ecrMovie.getScale());
+ _ecrMovie.redrawMovieWorld();
+ }
+ }
+}
+
+void NoradAlphaECRMonitor::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (isInteracting()) {
+ if (input.rightButtonDown())
+ skipToNextInterestingTime();
+ else if (input.leftButtonDown())
+ skipToPreviousInterestingTime();
+ else
+ InputHandler::handleInput(input, cursorSpot);
+ } else {
+ InputHandler::handleInput(input, cursorSpot);
+ }
+}
+
+void NoradAlphaECRMonitor::ecrSection1Finished() {
+ _ecrMovie.stop();
+ _ecrPanCallBack.setNotification(&_ecrSlideShowNotification);
+ _ecrPanCallBack.initCallBack(&_ecrPan, kCallBackAtExtremes);
+ _ecrPanCallBack.setCallBackFlag(kECRPanFinishedFlag);
+ _ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags);
+ _ecrPanCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _ecrPan.startDisplaying();
+ _ecrPan.show();
+
+ TimeScale scale = _ecrPan.getScale();
+ _ecrPan.setSegment(kPanStart * scale, kPanStop * scale);
+ _ecrPan.setTime(0);
+ _ecrPan.start();
+}
+
+void NoradAlphaECRMonitor::ecrPanFinished() {
+ _ecrPan.stop();
+ _ecrPan.stopDisplaying();
+ _ecrMovieCallBack.setCallBackFlag(kECRSection2FinishedFlag);
+ _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ TimeScale scale = _ecrMovie.getScale();
+ _ecrMovie.setSegment(kSection2Start * scale, kSection2Stop * scale);
+ _ecrMovie.start();
+}
+
+void NoradAlphaECRMonitor::ecrSection2Finished() {
+ _ecrMovie.stop();
+ _ecrMovie.stopDisplaying();
+}
+
+void NoradAlphaECRMonitor::openInteraction() {
+ // Initialize the security pan.
+ _ecrPan.initFromMovieFile("Images/Norad Alpha/Security Pan.pano");
+ _ecrPan.initMaskFromPICTFile("Images/Norad Alpha/Security Pan Mask");
+ _ecrPan.setBounds(Common::Rect(kECRPanLeft, kECRPanTop, kECRPanRight, kECRPanBottom));
+ _ecrPan.setDisplayOrder(kECRPanOrder);
+ _ecrPan.setScale(15); // 15 fps.
+
+ // Begin the lame ECR slide show.
+ // clone2727: I didn't say it :P
+ _ecrMovie.initFromMovieFile("Images/Norad Alpha/ECR Monitor Movie");
+
+ _ecrMovieCallBack.setNotification(&_ecrSlideShowNotification);
+ _ecrMovieCallBack.initCallBack(&_ecrMovie, kCallBackAtExtremes);
+ _ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag);
+
+ _ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags);
+ _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ _ecrMovie.moveElementTo(kECRSlideShowLeft, kECRSlideShowTop);
+ _ecrMovie.setDisplayOrder(kECRMonitorOrder);
+ _ecrMovie.startDisplaying();
+ _ecrMovie.show();
+ _ecrMovie.redrawMovieWorld();
+
+ TimeScale scale = _ecrMovie.getScale();
+ _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1);
+
+ _ecrMovie.start();
+}
+
+void NoradAlphaECRMonitor::closeInteraction() {
+ _ecrMovieCallBack.releaseCallBack();
+ _ecrMovie.stop();
+ _ecrMovie.stopDisplaying();
+ _ecrMovie.releaseMovie();
+ _ecrMovieCallBack.releaseCallBack();
+
+ _ecrPanCallBack.releaseCallBack();
+ _ecrPan.stop();
+ _ecrPan.stopDisplaying();
+ _ecrPan.releasePanorama();
+ _ecrPanCallBack.releaseCallBack();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h
new file mode 100644
index 0000000000..9e286ed337
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/notification.h"
+#include "pegasus/neighborhood/norad/alpha/panoramascroll.h"
+
+namespace Pegasus {
+
+class NoradAlphaECRMonitor : public GameInteraction, public NotificationReceiver {
+public:
+ NoradAlphaECRMonitor(Neighborhood *);
+ virtual ~NoradAlphaECRMonitor() {}
+
+ virtual void handleInput(const Input &, const Hotspot *);
+
+protected:
+ virtual void openInteraction();
+ virtual void closeInteraction();
+
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+
+ void ecrSection1Finished();
+ void ecrPanFinished();
+ void ecrSection2Finished();
+
+ int findCurrentInterestingTime();
+ void skipToNextInterestingTime();
+ void skipToPreviousInterestingTime();
+
+ Notification _ecrSlideShowNotification;
+ Movie _ecrMovie;
+ NotificationCallBack _ecrMovieCallBack;
+ PanoramaScroll _ecrPan;
+ NotificationCallBack _ecrPanCallBack;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp
new file mode 100644
index 0000000000..169f75f7d2
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp
@@ -0,0 +1,445 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/alpha/fillingstation.h"
+#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
+
+namespace Pegasus {
+
+static const NotificationFlags kFSPowerUpFinishedFlag = 1;
+static const NotificationFlags kFSSplashFinishedFlag = kFSPowerUpFinishedFlag << 1;
+static const NotificationFlags kFSIntakeWarningFinishedFlag = kFSSplashFinishedFlag << 1;
+static const NotificationFlags kFSIntakeHiliteFinishedFlag = kFSIntakeWarningFinishedFlag << 1;
+static const NotificationFlags kFSDispenseHiliteFinishedFlag = kFSIntakeHiliteFinishedFlag << 1;
+static const NotificationFlags kFSArHiliteFinishedFlag = kFSDispenseHiliteFinishedFlag << 1;
+static const NotificationFlags kFSCO2HiliteFinishedFlag = kFSArHiliteFinishedFlag << 1;
+static const NotificationFlags kFSHeHiliteFinishedFlag = kFSCO2HiliteFinishedFlag << 1;
+static const NotificationFlags kFSOHiliteFinishedFlag = kFSHeHiliteFinishedFlag << 1;
+static const NotificationFlags kFSNHiliteFinishedFlag = kFSOHiliteFinishedFlag << 1;
+
+static const NotificationFlags kFSNotificationFlags = kFSPowerUpFinishedFlag |
+ kFSSplashFinishedFlag |
+ kFSIntakeWarningFinishedFlag |
+ kFSIntakeHiliteFinishedFlag |
+ kFSDispenseHiliteFinishedFlag |
+ kFSArHiliteFinishedFlag |
+ kFSCO2HiliteFinishedFlag |
+ kFSHeHiliteFinishedFlag |
+ kFSOHiliteFinishedFlag |
+ kFSNHiliteFinishedFlag;
+
+static const int16 kNoState = 0;
+static const int16 kMainMenu = 1;
+static const int16 kWaitingForAttach = 2;
+static const int16 kDispenseMenu = 3;
+static const int16 kWaitingForDispense = 4;
+
+// Dummy itemIDs
+static const ItemID kCO2Item = 10000;
+static const ItemID kHeItem = 10001;
+
+// Interactive points.
+static const TimeValue kFSPowerUpStartStart = 0;
+static const TimeValue kFSPowerUpStartStop = 600;
+static const TimeValue kFSSplashStart = 600;
+static const TimeValue kFSSplashStop = 7800;
+static const TimeValue kFSSplashIntakeStart = 7800;
+static const TimeValue kFSSplashIntakeStop = 18600;
+
+static const TimeValue kFSMainMenu = 18600;
+static const TimeValue kFSIntakeHiliteStart = 19200;
+static const TimeValue kFSIntakeHiliteStop = 19800;
+static const TimeValue kFSDispenseHiliteStart = 19800;
+static const TimeValue kFSDispenseHiliteStop = 20400;
+
+static const TimeValue kFSDispenseMenu = 20400;
+
+static const TimeValue kFSArHiliteStart = 21000;
+static const TimeValue kFSArHiliteStop = 21600;
+static const TimeValue kFSArAttach = 21600;
+static const TimeValue kFSArFilledStart = 22200;
+static const TimeValue kFSArFilledStop = 25200;
+static const TimeValue kFSArIncompatibleStart = 25200;
+static const TimeValue kFSArIncompatibleStop = 30000;
+
+static const TimeValue kFSCO2HiliteStart = 30000;
+static const TimeValue kFSCO2HiliteStop = 30600;
+static const TimeValue kFSCO2Attach = 30600;
+static const TimeValue kFSCO2FilledStart = 31200;
+static const TimeValue kFSCO2FilledStop = 34200;
+static const TimeValue kFSCO2IncompatibleStart = 34200;
+static const TimeValue kFSCO2IncompatibleStop = 39000;
+
+static const TimeValue kFSHeHiliteStart = 39000;
+static const TimeValue kFSHeHiliteStop = 39600;
+static const TimeValue kFSHeAttach = 39600;
+static const TimeValue kFSHeFilledStart = 40200;
+static const TimeValue kFSHeFilledStop = 43200;
+static const TimeValue kFSHeIncompatibleStart = 43200;
+static const TimeValue kFSHeIncompatibleStop = 48000;
+
+static const TimeValue kFSOHiliteStart = 48000;
+static const TimeValue kFSOHiliteStop = 48600;
+static const TimeValue kFSOAttach = 48600;
+static const TimeValue kFSOFilledStart = 49200;
+static const TimeValue kFSOFilledStop = 52200;
+static const TimeValue kFSOIncompatibleStart = 52200;
+static const TimeValue kFSOIncompatibleStop = 57000;
+
+static const TimeValue kFSNHiliteStart = 57000;
+static const TimeValue kFSNHiliteStop = 57600;
+static const TimeValue kFSNAttach = 57600;
+static const TimeValue kFSNFilledStart = 58200;
+static const TimeValue kFSNFilledStop = 61200;
+static const TimeValue kFSNIncompatibleStart = 61200;
+static const TimeValue kFSNIncompatibleStop = 66000;
+
+static const TimeValue kFSIntakeMenu = 66000;
+static const TimeValue kFSIntakeInProgressStart = 66600;
+static const TimeValue kFSIntakeInProgressStop = 69600;
+
+NoradAlphaFillingStation::NoradAlphaFillingStation(Neighborhood *owner) : GameInteraction(kNoradFillingStationInteractionID, owner),
+ _rightSideMovie(kN01RightSideID), _rightSideNotification(kNoradFillingStationNotificationID, ((PegasusEngine *)g_engine)) {
+ _state = kNoState;
+}
+
+void NoradAlphaFillingStation::openInteraction() {
+ _rightSideMovie.initFromMovieFile("Images/Norad Alpha/N01W Right Side");
+ _rightSideMovie.moveElementTo(kNoradAlpha01RightSideLeft, kNoradAlpha01RightSideTop);
+ _rightSideMovie.setDisplayOrder(kN01RightSideOrder);
+ _rightSideMovie.startDisplaying();
+ _rightSideCallBack.setNotification(&_rightSideNotification);
+ _rightSideCallBack.initCallBack(&_rightSideMovie, kCallBackAtExtremes);
+ _rightSideCallBack.setCallBackFlag(kFSPowerUpFinishedFlag);
+ _rightSideNotification.notifyMe(this, kFSNotificationFlags, kFSNotificationFlags);
+ _rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _rightSideMovie.show();
+ _rightSideMovie.redrawMovieWorld();
+ _rightSideMovie.setSegment(kFSPowerUpStartStart, kFSPowerUpStartStop);
+}
+
+void NoradAlphaFillingStation::initInteraction() {
+ allowInput(false);
+
+ _rightSideMovie.setRate(2);
+}
+
+void NoradAlphaFillingStation::closeInteraction() {
+ _rightSideMovie.stop();
+ _rightSideMovie.stopDisplaying();
+ _rightSideMovie.releaseMovie();
+ _rightSideCallBack.releaseCallBack();
+ ((NoradAlpha *)getOwner())->turnOffFillingStation();
+}
+
+void NoradAlphaFillingStation::setStaticState(TimeValue time, int16 state) {
+ _rightSideMovie.stop();
+ _rightSideMovie.setSegment(0, _rightSideMovie.getDuration());
+ _rightSideMovie.setTime(time);
+ _rightSideMovie.redrawMovieWorld();
+ _state = state;
+ allowInput(true);
+}
+
+void NoradAlphaFillingStation::setSegmentState(TimeValue start, TimeValue stop, NotificationFlags flag, int16 state) {
+ _rightSideMovie.stop();
+ _rightSideMovie.setSegment(start, stop);
+ _rightSideMovie.setTime(start);
+ _rightSideCallBack.setCallBackFlag(flag);
+ _rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _state = state;
+ allowInput(false);
+ _rightSideMovie.setRate(2);
+}
+
+void NoradAlphaFillingStation::powerUpFinished() {
+ ((NoradAlpha *)getOwner())->turnOnFillingStation();
+ setSegmentState(kFSSplashStart, kFSSplashStop, kFSSplashFinishedFlag, kNoState);
+}
+
+void NoradAlphaFillingStation::splashFinished() {
+ if (GameState.getNoradGassed())
+ setSegmentState(kFSSplashIntakeStart, kFSSplashIntakeStop, kFSIntakeWarningFinishedFlag, kNoState);
+ else
+ intakeWarningFinished();
+}
+
+void NoradAlphaFillingStation::intakeWarningFinished() {
+ setStaticState(kFSMainMenu, kMainMenu);
+}
+
+void NoradAlphaFillingStation::showIntakeInProgress(uint16 numSeconds) {
+ if (numSeconds == 0) {
+ setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStop, kFSIntakeWarningFinishedFlag, kNoState);
+ Item *item = ((NoradAlpha *)getOwner())->getFillingItem();
+
+ if (item->getObjectID() == kGasCanister) {
+ GameState.setNoradGassed(true);
+ ((NoradAlpha *)getOwner())->loadAmbientLoops();
+ getOwner()->restoreStriding(kNorad03, kEast, kAltNoradAlphaNormal);
+ }
+ } else {
+ setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStart + _rightSideMovie.getScale() * numSeconds,
+ kFSIntakeWarningFinishedFlag, kNoState);
+ }
+}
+
+void NoradAlphaFillingStation::intakeHighlightFinished() {
+ _rightSideMovie.stop();
+
+ if (GameState.getNoradGassed()) {
+ showIntakeInProgress(2);
+ } else {
+ Item *item = ((NoradAlpha *)getOwner())->getFillingItem();
+ if (item)
+ showIntakeInProgress(0);
+ else
+ setStaticState(kFSIntakeMenu, kWaitingForAttach);
+ }
+}
+
+void NoradAlphaFillingStation::dispenseHighlightFinished() {
+ setStaticState(kFSDispenseMenu, kDispenseMenu);
+}
+
+void NoradAlphaFillingStation::dispenseGas() {
+ Item *item = ((NoradAlpha *)getOwner())->getFillingItem();
+
+ if (item) {
+ if (item->getObjectID() != _dispenseItemID)
+ switch (_dispenseItemID) {
+ case kArgonCanister:
+ setSegmentState(kFSArIncompatibleStart, kFSArIncompatibleStop,
+ kFSIntakeWarningFinishedFlag, kNoState);
+ break;
+ case kCO2Item:
+ setSegmentState(kFSCO2IncompatibleStart, kFSCO2IncompatibleStop,
+ kFSIntakeWarningFinishedFlag, kNoState);
+ break;
+ case kHeItem:
+ setSegmentState(kFSHeIncompatibleStart, kFSHeIncompatibleStop,
+ kFSIntakeWarningFinishedFlag, kNoState);
+ break;
+ case kAirMask:
+ setSegmentState(kFSOIncompatibleStart, kFSOIncompatibleStop,
+ kFSIntakeWarningFinishedFlag, kNoState);
+ break;
+ case kNitrogenCanister:
+ setSegmentState(kFSNIncompatibleStart, kFSNIncompatibleStop,
+ kFSIntakeWarningFinishedFlag, kNoState);
+ break;
+ }
+ else {
+ if (_dispenseItemID == kArgonCanister) {
+ setSegmentState(kFSArFilledStart, kFSArFilledStop, kFSIntakeWarningFinishedFlag, kNoState);
+ item->setItemState(kArgonFull);
+ GameState.setScoringFilledArgonCanister(true);
+ } else if (_dispenseItemID == kAirMask) {
+ setSegmentState(kFSOFilledStart, kFSOFilledStop, kFSIntakeWarningFinishedFlag, kNoState);
+ ((AirMask *)item)->refillAirMask();
+ GameState.setScoringFilledOxygenCanister(true);
+ } else if (_dispenseItemID == kNitrogenCanister) {
+ setSegmentState(kFSNFilledStart, kFSNFilledStop, kFSIntakeWarningFinishedFlag, kNoState);
+ item->setItemState(kNitrogenFull);
+ }
+ }
+ } else {
+ switch (_dispenseItemID) {
+ case kArgonCanister:
+ setStaticState(kFSArAttach, kWaitingForDispense);
+ break;
+ case kCO2Item:
+ setStaticState(kFSCO2Attach, kWaitingForDispense);
+ break;
+ case kHeItem:
+ setStaticState(kFSHeAttach, kWaitingForDispense);
+ break;
+ case kAirMask:
+ setStaticState(kFSOAttach, kWaitingForDispense);
+ break;
+ case kNitrogenCanister:
+ setStaticState(kFSNAttach, kWaitingForDispense);
+ break;
+ }
+ }
+}
+
+void NoradAlphaFillingStation::ArHighlightFinished() {
+ _dispenseItemID = kArgonCanister;
+ dispenseGas();
+}
+
+void NoradAlphaFillingStation::CO2HighlightFinished() {
+ _dispenseItemID = kCO2Item;
+ dispenseGas();
+}
+
+void NoradAlphaFillingStation::HeHighlightFinished() {
+ _dispenseItemID = kHeItem;
+ dispenseGas();
+}
+
+void NoradAlphaFillingStation::OHighlightFinished() {
+ _dispenseItemID = kAirMask;
+ dispenseGas();
+}
+
+void NoradAlphaFillingStation::NHighlightFinished() {
+ _dispenseItemID = kNitrogenCanister;
+ dispenseGas();
+}
+
+void NoradAlphaFillingStation::receiveNotification(Notification *, const NotificationFlags flags) {
+ switch (flags) {
+ case kFSPowerUpFinishedFlag:
+ powerUpFinished();
+ break;
+ case kFSSplashFinishedFlag:
+ splashFinished();
+ break;
+ case kFSIntakeWarningFinishedFlag:
+ intakeWarningFinished();
+ break;
+ case kFSIntakeHiliteFinishedFlag:
+ intakeHighlightFinished();
+ break;
+ case kFSDispenseHiliteFinishedFlag:
+ dispenseHighlightFinished();
+ break;
+ case kFSArHiliteFinishedFlag:
+ ArHighlightFinished();
+ break;
+ case kFSCO2HiliteFinishedFlag:
+ CO2HighlightFinished();
+ break;
+ case kFSHeHiliteFinishedFlag:
+ HeHighlightFinished();
+ break;
+ case kFSOHiliteFinishedFlag:
+ OHighlightFinished();
+ break;
+ case kFSNHiliteFinishedFlag:
+ NHighlightFinished();
+ break;
+ }
+}
+
+void NoradAlphaFillingStation::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+void NoradAlphaFillingStation::clickInIntake() {
+ setSegmentState(kFSIntakeHiliteStart, kFSIntakeHiliteStop, kFSIntakeHiliteFinishedFlag, kNoState);
+}
+
+void NoradAlphaFillingStation::clickInDispense() {
+ setSegmentState(kFSDispenseHiliteStart, kFSDispenseHiliteStop, kFSDispenseHiliteFinishedFlag, kNoState);
+}
+
+void NoradAlphaFillingStation::clickInAr() {
+ setSegmentState(kFSArHiliteStart, kFSArHiliteStop, kFSArHiliteFinishedFlag, kNoState);
+}
+
+void NoradAlphaFillingStation::clickInCO2() {
+ setSegmentState(kFSCO2HiliteStart, kFSCO2HiliteStop, kFSCO2HiliteFinishedFlag, kNoState);
+}
+
+void NoradAlphaFillingStation::clickInHe() {
+ setSegmentState(kFSHeHiliteStart, kFSHeHiliteStop, kFSHeHiliteFinishedFlag, kNoState);
+}
+
+void NoradAlphaFillingStation::clickInO() {
+ setSegmentState(kFSOHiliteStart, kFSOHiliteStop, kFSOHiliteFinishedFlag, kNoState);
+}
+
+void NoradAlphaFillingStation::clickInN() {
+ setSegmentState(kFSNHiliteStart, kFSNHiliteStop, kFSNHiliteFinishedFlag, kNoState);
+}
+
+void NoradAlphaFillingStation::clickInHotspot(const Input &input, const Hotspot *spot) {
+ GameInteraction::clickInHotspot(input, spot);
+
+ switch (spot->getObjectID()) {
+ case kNorad01IntakeSpotID:
+ clickInIntake();
+ break;
+ case kNorad01DispenseSpotID:
+ clickInDispense();
+ break;
+ case kNorad01ArSpotID:
+ clickInAr();
+ break;
+ case kNorad01CO2SpotID:
+ clickInCO2();
+ break;
+ case kNorad01HeSpotID:
+ clickInHe();
+ break;
+ case kNorad01OSpotID:
+ clickInO();
+ break;
+ case kNorad01NSpotID:
+ clickInN();
+ break;
+ }
+}
+
+void NoradAlphaFillingStation::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ switch (_state) {
+ case kMainMenu:
+ g_allHotspots.activateOneHotspot(kNorad01IntakeSpotID);
+ g_allHotspots.activateOneHotspot(kNorad01DispenseSpotID);
+ break;
+ case kDispenseMenu:
+ g_allHotspots.activateOneHotspot(kNorad01ArSpotID);
+ g_allHotspots.activateOneHotspot(kNorad01CO2SpotID);
+ g_allHotspots.activateOneHotspot(kNorad01HeSpotID);
+ g_allHotspots.activateOneHotspot(kNorad01OSpotID);
+ g_allHotspots.activateOneHotspot(kNorad01NSpotID);
+ break;
+ }
+}
+
+void NoradAlphaFillingStation::newFillingItem(Item *item) {
+ switch (_state) {
+ case kWaitingForAttach:
+ if (item)
+ showIntakeInProgress(0);
+ break;
+ case kWaitingForDispense:
+ dispenseGas();
+ break;
+ default:
+ break;
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.h b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h
new file mode 100644
index 0000000000..eb2088e373
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+
+namespace Pegasus {
+
+class Item;
+
+class NoradAlphaFillingStation : public GameInteraction, public NotificationReceiver {
+public:
+ NoradAlphaFillingStation(Neighborhood *);
+ virtual ~NoradAlphaFillingStation() {}
+
+ virtual void handleInput(const Input &, const Hotspot *);
+
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+ virtual void activateHotspots();
+
+ void newFillingItem(Item *);
+
+protected:
+ void receiveNotification(Notification *, const NotificationFlags);
+
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+
+ void powerUpFinished();
+ void splashFinished();
+ void intakeWarningFinished();
+ void intakeHighlightFinished();
+ void dispenseHighlightFinished();
+ void ArHighlightFinished();
+ void CO2HighlightFinished();
+ void HeHighlightFinished();
+ void OHighlightFinished();
+ void NHighlightFinished();
+
+ void showIntakeInProgress(uint16);
+
+ void clickInIntake();
+ void clickInDispense();
+ void clickInAr();
+ void clickInCO2();
+ void clickInHe();
+ void clickInO();
+ void clickInN();
+
+ void dispenseGas();
+
+ void setStaticState(TimeValue, int16);
+ void setSegmentState(TimeValue, TimeValue, NotificationFlags, int16);
+
+ Movie _rightSideMovie;
+ Notification _rightSideNotification;
+ NotificationCallBack _rightSideCallBack;
+ int16 _state;
+ ItemID _dispenseItemID;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp
new file mode 100644
index 0000000000..e4a5e26473
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp
@@ -0,0 +1,763 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/subcontrolroom.h"
+#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h"
+#include "pegasus/neighborhood/norad/alpha/fillingstation.h"
+#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
+
+namespace Pegasus {
+
+const uint32 NoradAlpha::_noradAlphaClawExtras[22] = {
+ kN22ClawFromAToB,
+ kN22ClawALoop,
+ kN22ClawAPinch,
+ kN22ClawACounterclockwise,
+ kN22ClawAClockwise,
+ kN22ClawFromBToA,
+ kN22ClawFromBToC,
+ kN22ClawFromBToD,
+ kN22ClawBLoop,
+ kN22ClawBPinch,
+ kN22ClawBCounterclockwise,
+ kN22ClawBClockwise,
+ kN22ClawFromCToB,
+ kN22ClawCLoop,
+ kN22ClawCPinch,
+ kN22ClawCCounterclockwise,
+ kN22ClawCClockwise,
+ kN22ClawFromDToB,
+ kN22ClawDLoop,
+ kN22ClawDPinch,
+ kN22ClawDCounterclockwise,
+ kN22ClawDClockwise
+};
+
+NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Alpha", kNoradAlphaID) {
+ _elevatorUpRoomID = kNorad11South;
+ _elevatorDownRoomID = kNorad12South;
+ _elevatorUpSpotID = kNorad12ElevatorUpSpotID;
+ _elevatorDownSpotID = kNorad11ElevatorDownSpotID;
+
+ _subRoomEntryRoom1 = kNorad10;
+ _subRoomEntryDir1 = kEast;
+ _subRoomEntryRoom2 = kNorad21;
+ _subRoomEntryDir2 = kWest;
+ _upperPressureDoorRoom = kNorad10East;
+ _lowerPressureDoorRoom = kNorad21West;
+
+ _upperPressureDoorUpSpotID = kAlphaUpperPressureDoorUpSpotID;
+ _upperPressureDoorDownSpotID = kAlphaUpperPressureDoorDownSpotID;
+ _upperPressureDoorAbortSpotID = kNorad10EastOutSpotID;
+
+ _lowerPressureDoorUpSpotID = kAlphaLowerPressureDoorUpSpotID;
+ _lowerPressureDoorDownSpotID = kAlphaLowerPressureDoorDownSpotID;
+ _lowerPressureDoorAbortSpotID = kNorad21WestOutSpotID;
+
+ _pressureSoundIn = kPressureDoorIntro1In;
+ _pressureSoundOut = kPressureDoorIntro1Out;
+ _equalizeSoundIn = kPressureDoorIntro2In;
+ _equalizeSoundOut = kPressureDoorIntro2Out;
+ _accessDeniedIn = kAlphaAccessDeniedIn;
+ _accessDeniedOut = kAlphaAccessDeniedOut;
+
+ _platformRoom = kNorad19West;
+ _subControlRoom = kNorad22West;
+
+ _subPrepFailed = false;
+
+ setIsItemTaken(kGasCanister);
+}
+
+void NoradAlpha::init() {
+ Norad::init();
+
+ Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
+ HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasCanisterSpotID);
+ hotspotEntry->hotspotItem = kGasCanister;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kN01ArgonCanisterSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
+ hotspotEntry = findHotspotEntry(kN01ArgonCanisterSpotID);
+ hotspotEntry->hotspotItem = kArgonCanister;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kN01NitrogenCanisterSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
+ hotspotEntry = findHotspotEntry(kN01NitrogenCanisterSpotID);
+ hotspotEntry->hotspotItem = kNitrogenCanister;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kN01AirMaskSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
+ hotspotEntry = findHotspotEntry(kN01AirMaskSpotID);
+ hotspotEntry->hotspotItem = kAirMask;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasOutletSpotID);
+ hotspot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag);
+}
+
+void NoradAlpha::start() {
+ if (g_energyMonitor) {
+ g_energyMonitor->stopEnergyDraining();
+ g_energyMonitor->restoreLastEnergyValue();
+ _vm->resetEnergyDeathReason();
+ g_energyMonitor->startEnergyDraining();
+ }
+
+ NeighborhoodID itemNeighborhood;
+ RoomID itemRoom;
+ DirectionConstant itemDirection;
+
+ Item *item = (Item *)_vm->getAllItems().findItemByID(kGasCanister);
+ item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
+
+ if (itemNeighborhood == getObjectID()) {
+ _fillingStationItem = item;
+ } else {
+ item = (Item *)_vm->getAllItems().findItemByID(kAirMask);
+ item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
+
+ if (itemNeighborhood == getObjectID()) {
+ _fillingStationItem = item;
+ } else {
+ item = (Item *)_vm->getAllItems().findItemByID(kNitrogenCanister);
+ item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
+
+ if (itemNeighborhood == getObjectID()) {
+ _fillingStationItem = item;
+ } else {
+ item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister);
+ item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
+ if (itemNeighborhood == getObjectID())
+ _fillingStationItem = item;
+ else
+ _fillingStationItem = 0;
+ }
+ }
+ }
+
+ if (!GameState.getNoradGassed())
+ forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal);
+
+ GameState.setNoradArrivedFromSub(false);
+ Norad::start();
+}
+
+void NoradAlpha::setUpAIRules() {
+ Neighborhood::setUpAIRules();
+
+ if (g_AIArea) {
+ AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN01WD1", false);
+ AIHasItemCondition *hasGasCanisterCondition = new AIHasItemCondition(kGasCanister);
+ AIRule *rule = new AIRule(hasGasCanisterCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+ }
+}
+
+bool NoradAlpha::okayToJump() {
+ bool result = Neighborhood::okayToJump();
+
+ if (!result)
+ playSpotSoundSync(kAlphaCantTransportIn, kAlphaCantTransportOut);
+
+ return result;
+}
+
+void NoradAlpha::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) {
+ if (entry.extra == kNorad19ExitToSub) {
+ compassMove.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, entry.movieStart, 270 + kSubPlatformCompassAngle,
+ entry.movieEnd, 90 + 20 + 360);
+ compassMove.insertFaderKnot(entry.movieStart + 10 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle);
+ compassMove.insertFaderKnot(entry.movieStart + 29 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20);
+ compassMove.insertFaderKnot(entry.movieStart + 52 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20);
+ compassMove.insertFaderKnot(entry.movieStart + 84 * kNoradAlphaFrameDuration, 360 + 90);
+ compassMove.insertFaderKnot(entry.movieStart + 198 * kNoradAlphaFrameDuration, 360 + 90);
+ compassMove.insertFaderKnot(entry.movieStart + 270 * kNoradAlphaFrameDuration, 360 + 90 + 15);
+ compassMove.insertFaderKnot(entry.movieStart + 280 * kNoradAlphaFrameDuration, 360 + 90 + 20);
+ } else {
+ Norad::getExtraCompassMove(entry, compassMove);
+ }
+}
+
+void NoradAlpha::playClawMonitorIntro() {
+ playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut);
+}
+
+GameInteraction *NoradAlpha::makeInteraction(const InteractionID interactionID) {
+ switch (interactionID) {
+ case kNoradECRMonitorInteractionID:
+ return new NoradAlphaECRMonitor(this);
+ case kNoradFillingStationInteractionID:
+ return new NoradAlphaFillingStation(this);
+ }
+
+ return Norad::makeInteraction(interactionID);
+}
+
+void NoradAlpha::loadAmbientLoops() {
+ // clone2727 would like to point out that the following comment does not quite
+ // match the code logic below
+
+/*
+ Logic:
+
+ loop sound 1:
+ if gassed,
+ play warning loop of some sort
+ else
+ play nothing
+ loop sound 2:
+ if gassed and not wearing air mask
+ if in ECR
+ play breathing water loop
+ else
+ play breathing
+ else
+ if in ECR
+ play water loop
+ if at N07 north
+ play unmanned loop
+*/
+
+ if (!GameState.getNoradSeenTimeStream())
+ return;
+
+ RoomID room = GameState.getCurrentRoom();
+ if (GameState.getNoradGassed()) {
+ if (room >= kNorad11 && room <= kNorad19West)
+ loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
+ else if (room >= kNorad21 && room <= kNorad22West)
+ loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
+ else
+ loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume);
+ } else {
+ loadLoopSound1("");
+ }
+
+ if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) {
+ if (room >= kNorad01 && room <= kNorad01West) {
+ loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume);
+ } else if (room == kNorad02) {
+ if (GameState.isCurrentDoorOpen())
+ loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume);
+ else
+ loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0);
+ } else {
+ loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0);
+ }
+ } else {
+ if (room >= kNorad01 && room <= kNorad01West) {
+ loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2);
+ } else if (room == kNorad02) {
+ if (GameState.isCurrentDoorOpen())
+ loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2);
+ else
+ loadLoopSound2("");
+ } else {
+ loadLoopSound2("");
+ }
+ }
+
+}
+
+void NoradAlpha::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kNorad02, kEast):
+ case MakeRoomView(kNorad06, kEast):
+ case MakeRoomView(kNorad11, kEast):
+ case MakeRoomView(kNorad15, kEast):
+ case MakeRoomView(kNorad19, kWest):
+ case MakeRoomView(kNorad21, kSouth):
+ makeContinuePoint();
+ break;
+ }
+}
+
+void NoradAlpha::arriveAt(const RoomID room, const DirectionConstant direction) {
+ Norad::arriveAt(room, direction);
+
+ switch (GameState.getCurrentRoom()) {
+ case kNorad01:
+ arriveAtNorad01();
+ break;
+ case kNorad01East:
+ arriveAtNorad01East();
+ break;
+ case kNorad01West:
+ arriveAtNorad01West();
+ break;
+ case kNorad04:
+ arriveAtNorad04();
+ break;
+ case kNorad07North:
+ GameState.setScoringSawUnconsciousOperator(true);
+ break;
+ case kNorad11:
+ GameState.setScoringWentThroughPressureDoor(true);
+ break;
+ case kNorad22:
+ arriveAtNorad22();
+ break;
+ }
+}
+
+void NoradAlpha::arriveAtNorad01() {
+ if (!GameState.getNoradSeenTimeStream() && GameState.getCurrentDirection() == kSouth) {
+ GameState.setNoradN22MessagePlayed(false);
+ requestExtraSequence(kNoradArriveFromTSA, kExtraCompletedFlag, kFilterNoInput);
+ // You are no match for me, human.
+ requestExtraSequence(kNorad01RobotTaunt, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+void NoradAlpha::arriveAtNorad01East() {
+ GameState.setScoringSawSecurityMonitor(true);
+ newInteraction(kNoradECRMonitorInteractionID);
+}
+
+void NoradAlpha::arriveAtNorad01West() {
+ newInteraction(kNoradFillingStationInteractionID);
+}
+
+void NoradAlpha::arriveAtNorad04() {
+ if (GameState.getCurrentDirection() == kEast && !GameState.getNoradGassed())
+ playDeathExtra(kNorad04EastDeath, kDeathWokeUpNorad);
+}
+
+void NoradAlpha::arriveAtNorad22() {
+ if (!GameState.getNoradN22MessagePlayed() && GameState.getCurrentDirection() == kSouth) {
+ startExtraSequence(kNorad22SouthIntro, kExtraCompletedFlag, kFilterNoInput);
+ GameState.setNoradN22MessagePlayed(true);
+ }
+}
+
+void NoradAlpha::bumpIntoWall() {
+ requestSpotSound(kAlphaBumpIntoWallIn, kAlphaBumpIntoWallOut, kFilterNoInput, 0);
+ Neighborhood::bumpIntoWall();
+}
+
+void NoradAlpha::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ if ((flags & kExtraCompletedFlag) != 0) {
+ switch (_lastExtra) {
+ case kNoradArriveFromTSA:
+ GameState.setNoradSeenTimeStream(true);
+ loadAmbientLoops();
+ break;
+ case kNorad01RobotTaunt:
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN01SB", false, kWarningInterruption);
+ _interruptionFilter = kFilterAllInput;
+ makeContinuePoint();
+ break;
+ }
+ }
+
+ Norad::receiveNotification(notification, flags);
+
+ if ((flags & kExtraCompletedFlag) != 0) {
+ switch (_lastExtra) {
+ case kNorad22SouthIntro:
+ loopExtraSequence(kNorad22SouthReply);
+ playSpotSoundSync(kN22ReplyIn, kN22ReplyOut);
+ startExtraSequence(kNorad22SouthFinish, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kNorad22SouthFinish:
+ _interruptionFilter = kFilterAllInput;
+ // Force ArriveAt to do its thing...
+ GameState.setCurrentRoom(kNorad21);
+ arriveAt(kNorad22, kSouth);
+ break;
+ }
+ }
+
+ g_AIArea->checkMiddleArea();
+}
+
+void NoradAlpha::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) {
+ Norad::getZoomEntry(spotID, entry);
+
+ ExtraTable::Entry extra;
+
+ if (spotID == kNorad01GasSpotID) {
+ if (_fillingStationItem) {
+ if (_fillingStationItem->getObjectID() == kGasCanister) {
+ getExtraEntry(kNorad01ZoomInWithGasCanister, extra);
+ entry.movieStart = extra.movieStart;
+ entry.movieEnd = extra.movieEnd;
+ } else {
+ entry.clear();
+ }
+ }
+ } else if (spotID == kNorad01GasOutSpotID) {
+ if (_fillingStationItem) {
+ if (_fillingStationItem->getObjectID() == kGasCanister) {
+ getExtraEntry(kNorad01ZoomOutWithGasCanister, extra);
+ entry.movieStart = extra.movieStart;
+ entry.movieEnd = extra.movieEnd;
+ } else {
+ entry.clear();
+ }
+ }
+ }
+}
+
+TimeValue NoradAlpha::getViewTime(const RoomID room, const DirectionConstant direction) {
+ ExtraTable::Entry entry;
+
+ if (room == kNorad01 && direction == kSouth && !GameState.getNoradSeenTimeStream()) {
+ getExtraEntry(kNoradArriveFromTSA, entry);
+ return entry.movieStart;
+ }
+
+ if (room == kNorad01 && direction == kWest) {
+ if (!_fillingStationItem) {
+ return Norad::getViewTime(room, direction);
+ } else {
+ getExtraEntry(kN01WGasCanister, entry);
+ return entry.movieStart;
+ }
+ } else if (room == kNorad01West && direction == kWest) {
+ uint32 extraID = 0xffffffff;
+ if (_fillingStationItem) {
+ switch (_fillingStationItem->getObjectID()) {
+ case kArgonCanister:
+ if (GameState.getNoradFillingStationOn())
+ extraID = kN01WZArgonCanisterLit;
+ else
+ extraID = kN01WZArgonCanisterDim;
+ break;
+ case kGasCanister:
+ if (GameState.getNoradFillingStationOn())
+ extraID = kN01WZGasCanisterLit;
+ else
+ extraID = kN01WZGasCanisterDim;
+ break;
+ case kAirMask:
+ if (GameState.getNoradFillingStationOn())
+ extraID = kN01WZAirMaskLit;
+ else
+ extraID = kN01WZAirMaskDim;
+ break;
+ case kNitrogenCanister:
+ if (GameState.getNoradFillingStationOn())
+ extraID = kN01WZNitrogenCanisterLit;
+ else
+ extraID = kN01WZNitrogenCanisterDim;
+ break;
+ default:
+ // Should never happen.
+ break;
+ }
+ } else if (GameState.getNoradFillingStationOn()) {
+ extraID = kN01WZEmptyLit;
+ }
+
+ if (extraID == 0xffffffff) {
+ return Norad::getViewTime(room, direction);
+ } else {
+ getExtraEntry(extraID, entry);
+ return entry.movieStart;
+ }
+ }
+
+ return Norad::getViewTime(room, direction);
+}
+
+void NoradAlpha::turnOnFillingStation() {
+ if (GameState.getCurrentRoom() == kNorad01West && !GameState.getNoradFillingStationOn()) {
+ GameState.setNoradFillingStationOn(true);
+ updateViewFrame();
+ }
+}
+
+void NoradAlpha::turnOffFillingStation() {
+ if (GameState.getCurrentRoom() == kNorad01West && GameState.getNoradFillingStationOn()) {
+ GameState.setNoradFillingStationOn(false);
+ updateViewFrame();
+ }
+}
+
+void NoradAlpha::activateHotspots() {
+ Norad::activateHotspots();
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kNorad01West, kWest):
+ if (_vm->getDragType() == kDragInventoryUse) {
+ if (!_fillingStationItem) {
+ ItemID itemID = _vm->getDraggingItem()->getObjectID();
+ if (itemID == kArgonCanister || itemID == kGasCanister || itemID == kAirMask ||
+ itemID == kNitrogenCanister)
+ _vm->getAllHotspots().activateOneHotspot(kN01GasOutletSpotID);
+ }
+ } else {
+ HotSpotID spotID;
+
+ if (_fillingStationItem) {
+ switch (_fillingStationItem->getObjectID()) {
+ case kArgonCanister:
+ spotID = kN01ArgonCanisterSpotID;
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID);
+ break;
+ case kGasCanister:
+ spotID = kN01GasCanisterSpotID;
+ break;
+ case kAirMask:
+ spotID = kN01AirMaskSpotID;
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID);
+ break;
+ case kNitrogenCanister:
+ spotID = kN01NitrogenCanisterSpotID;
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID);
+ break;
+ default:
+ // Should never happen.
+ spotID = kNoHotSpotID;
+ break;
+ }
+ _vm->getAllHotspots().activateOneHotspot(spotID);
+ }
+ }
+ break;
+ case MakeRoomView(kNorad10, kEast):
+ if (GameState.isCurrentDoorOpen())
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad10DoorSpotID);
+ break;
+ case MakeRoomView(kNorad21, kWest):
+ if (GameState.isCurrentDoorOpen())
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad21WestSpotID);
+ break;
+ }
+}
+
+void NoradAlpha::clickInHotspot(const Input &input, const Hotspot *cursorSpot) {
+ Norad::clickInHotspot(input, cursorSpot);
+
+ if (_vm->getDragType() == kDragInventoryUse) {
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad01West, kWest)) {
+ Item *item = _vm->getDraggingItem();
+ if (item->getObjectID() == kAirMask || item->getObjectID() == kArgonCanister ||
+ item->getObjectID() == kNitrogenCanister || item->getObjectID() == kGasCanister) {
+ HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasOutletSpotID);
+ hotspotEntry->hotspotItem = item->getObjectID();
+ }
+ }
+ }
+}
+
+void NoradAlpha::takeItemFromRoom(Item *item) {
+ if (GameState.getCurrentRoom() == kNorad01West) {
+ if (_fillingStationItem == item) {
+ _fillingStationItem = 0;
+ GameState.setNoradGassed(false);
+ loadAmbientLoops();
+ ((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(0);
+ forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal);
+ }
+ }
+
+ Norad::takeItemFromRoom(item);
+}
+
+void NoradAlpha::dropItemIntoRoom(Item *item, Hotspot *droppedSpot) {
+ if (GameState.getCurrentRoom() == kNorad01West) {
+ if (!_fillingStationItem) {
+ _fillingStationItem = item;
+ ((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(item);
+ }
+ }
+
+ Norad::dropItemIntoRoom(item, droppedSpot);
+}
+
+void NoradAlpha::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID,
+ HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID,
+ HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) {
+ outSpotID = kNorad22MonitorOutSpotID;
+ prepSpotID = kNorad22LaunchPrepSpotID;
+ clawControlSpotID = kNorad22ClawControlSpotID;
+ pinchClawSpotID = kNorad22ClawPinchSpotID;
+ moveClawDownSpotID = kNorad22ClawDownSpotID;
+ moveClawRightSpotID = kNorad22ClawRightSpotID;
+ moveClawLeftSpotID = kNorad22ClawLeftSpotID;
+ moveClawUpSpotID = kNorad22ClawUpSpotID;
+ clawCCWSpotID = kNorad22ClawCCWSpotID;
+ clawCWSpotID = kNorad22ClawCWSpotID;
+ clawPosition = kClawAtD;
+ clawExtraIDs = _noradAlphaClawExtras;
+}
+
+Hotspot *NoradAlpha::getItemScreenSpot(Item *item, DisplayElement *element) {
+ switch (item->getObjectID()) {
+ case kGasCanister:
+ return _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID);
+ case kAirMask:
+ return _vm->getAllHotspots().findHotspotByID(kN01AirMaskSpotID);
+ case kArgonCanister:
+ return _vm->getAllHotspots().findHotspotByID(kN01ArgonCanisterSpotID);
+ case kNitrogenCanister:
+ return _vm->getAllHotspots().findHotspotByID(kN01NitrogenCanisterSpotID);
+ }
+
+ return Norad::getItemScreenSpot(item, element);
+}
+
+Common::String NoradAlpha::getEnvScanMovie() {
+ Common::String movieName = Neighborhood::getEnvScanMovie();
+
+ if (movieName.empty()) {
+ RoomID room = GameState.getCurrentRoom();
+ if (room >= kNorad01 && room <= kNorad01West)
+ return "Images/AI/Norad/XNE1";
+ else if ((room >= kNorad02 && room <= kNorad19West))
+ return "Images/AI/Norad/XNE2";
+
+ return "Images/AI/Norad/XNE3";
+ }
+
+ return movieName;
+}
+
+uint NoradAlpha::getNumHints() {
+ uint numHints = Neighborhood::getNumHints();
+
+ if (numHints == 0) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kNorad01, kNorth):
+ case MakeRoomView(kNorad01, kSouth):
+ case MakeRoomView(kNorad01, kEast):
+ case MakeRoomView(kNorad01, kWest):
+ case MakeRoomView(kNorad01East, kEast):
+ case MakeRoomView(kNorad01West, kWest):
+ if (GameState.getNoradGassed()) {
+ if (g_airMask->isAirFilterOn())
+ numHints = 0;
+ else
+ numHints = 3;
+ } else {
+ numHints = 2;
+ }
+ break;
+ case MakeRoomView(kNorad19West, kWest):
+ if (getSubPrepFailed() && GameState.getNoradSubPrepState() != kSubPrepped)
+ numHints = 1;
+ break;
+ case MakeRoomView(kNorad22, kWest):
+ numHints = 1;
+ break;
+ }
+ }
+
+ return numHints;
+}
+
+Common::String NoradAlpha::getHintMovie(uint hintNum) {
+ Common::String movieName = Neighborhood::getHintMovie(hintNum);
+
+ if (movieName.empty()) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kNorad01, kNorth):
+ case MakeRoomView(kNorad01, kSouth):
+ case MakeRoomView(kNorad01, kEast):
+ case MakeRoomView(kNorad01, kWest):
+ case MakeRoomView(kNorad01East, kEast):
+ case MakeRoomView(kNorad01West, kWest):
+ switch (hintNum) {
+ case 1:
+ if (GameState.getNoradGassed())
+ return "Images/AI/Norad/XN01SW";
+
+ return "Images/AI/Norad/XN01WD2";
+ case 2:
+ if (GameState.getNoradGassed()) {
+ if (_vm->playerHasItemID(kAirMask))
+ // Mask must not be on if we get here...
+ return "Images/AI/Globals/XGLOB1A";
+
+ return "Images/AI/Globals/XGLOB3D";
+ }
+
+ return "Images/AI/Globals/XGLOB5C";
+ case 3:
+ return "Images/AI/Norad/XN01SH";
+ }
+ break;
+ case MakeRoomView(kNorad19West, kWest):
+ return "Images/AI/Norad/XN19NH";
+ case MakeRoomView(kNorad22, kWest):
+ return "Images/AI/Globals/XGLOB1C";
+ }
+ }
+
+ return movieName;
+}
+
+void NoradAlpha::closeDoorOffScreen(const RoomID room, const DirectionConstant) {
+ switch (room) {
+ case kNorad12:
+ case kNorad13:
+ case kNorad18:
+ case kNorad19:
+ playSpotSoundSync(kAlphaElevatorDoorCloseIn, kAlphaElevatorDoorCloseOut);
+ break;
+ default:
+ playSpotSoundSync(kAlphaRegDoorCloseIn, kAlphaRegDoorCloseOut);
+ break;
+ }
+}
+
+void NoradAlpha::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) {
+ if (room == kNorad01 && direction == kSouth)
+ spotEntry.clear();
+ else
+ Norad::findSpotEntry(room, direction, flags, spotEntry);
+}
+
+bool NoradAlpha::canSolve() {
+ return Norad::canSolve() || getHintMovie(1) == "Images/AI/Norad/XN01SW";
+}
+
+void NoradAlpha::doSolve() {
+ Norad::doSolve();
+
+ if (getHintMovie(1) == "Images/AI/Norad/XN01SW") {
+ _vm->addItemToInventory(g_airMask);
+ g_airMask->putMaskOn();
+ }
+}
+
+Common::String NoradAlpha::getNavMovieName() {
+ return "Images/Norad Alpha/Norad Alpha.movie";
+}
+
+Common::String NoradAlpha::getSoundSpotsName() {
+ return "Sounds/Norad/Norad Alpha Spots";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.h b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h
new file mode 100644
index 0000000000..582d6c2bb3
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h
@@ -0,0 +1,115 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H
+
+#include "pegasus/neighborhood/norad/norad.h"
+
+namespace Pegasus {
+
+class Item;
+
+class NoradAlpha : public Norad {
+public:
+ NoradAlpha(InputHandler *, PegasusEngine *);
+ virtual ~NoradAlpha() {}
+
+ virtual void init();
+ void start();
+
+ virtual bool okayToJump();
+
+ void playClawMonitorIntro();
+
+ void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &);
+
+ void turnOnFillingStation();
+ void turnOffFillingStation();
+ Item *getFillingItem() { return _fillingStationItem; }
+ bool gasCanisterIntake();
+
+ virtual void takeItemFromRoom(Item *);
+ virtual void dropItemIntoRoom(Item *, Hotspot *);
+
+ virtual GameInteraction *makeInteraction(const InteractionID);
+
+ virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID,
+ HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID,
+ HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID,
+ HotSpotID &clawCWSpotID, uint32 &, const uint32 *&);
+
+ void loadAmbientLoops();
+
+ Common::String getEnvScanMovie();
+ uint getNumHints();
+ Common::String getHintMovie(uint);
+ void setUpAIRules();
+
+ void setSubPrepFailed(bool value) { _subPrepFailed = value; }
+ bool getSubPrepFailed() { return _subPrepFailed; }
+
+ void closeDoorOffScreen(const RoomID, const DirectionConstant);
+ void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &);
+ void clickInHotspot(const Input &, const Hotspot *);
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+ bool canSolve();
+ void doSolve();
+
+protected:
+ static const uint32 _noradAlphaClawExtras[22];
+
+ virtual void arriveAtNorad01();
+ virtual void arriveAtNorad01East();
+ virtual void arriveAtNorad01West();
+ virtual void arriveAtNorad04();
+ virtual void arriveAtNorad22();
+
+ virtual void arriveAt(const RoomID, const DirectionConstant);
+
+ virtual void getZoomEntry(const HotSpotID, ZoomTable::Entry &);
+ virtual TimeValue getViewTime(const RoomID, const DirectionConstant);
+
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+
+ virtual void activateHotspots();
+
+ Hotspot *getItemScreenSpot(Item *, DisplayElement *);
+
+ void bumpIntoWall();
+
+ Item *_fillingStationItem;
+
+ bool _subPrepFailed;
+
+ Common::String getSoundSpotsName();
+ Common::String getNavMovieName();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/alpha/panorama.cpp b/engines/pegasus/neighborhood/norad/alpha/panorama.cpp
new file mode 100644
index 0000000000..5a717a84e7
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/panorama.cpp
@@ -0,0 +1,239 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/macresman.h"
+#include "common/stream.h"
+#include "pegasus/neighborhood/norad/alpha/panorama.h"
+
+namespace Pegasus {
+
+Panorama::Panorama() : _panoramaMovie(kNoDisplayElement) {
+ blankFields();
+}
+
+Panorama::~Panorama() {
+ releasePanorama();
+}
+
+void Panorama::blankFields() {
+ _viewBounds = Common::Rect();
+ _drawBounds = Common::Rect();
+ _mask = 0;
+ _panoramaWidth = 0;
+ _panoramaHeight = 0;
+ _stripWidth = 0;
+ _stripLeft = -1;
+ _stripRight = -1;
+}
+
+void Panorama::releasePanorama() {
+ if (_panoramaMovie.isMovieValid()) {
+ _panoramaMovie.releaseMovie();
+ _panoramaWorld.deallocateSurface();
+ blankFields();
+ }
+}
+
+static const uint32 kPanoramaResType = MKTAG('P', 'a', 'n', 'I'); // Panorama Information.
+static const uint16 kPanoramaResID = 128;
+
+void Panorama::initFromMovieFile(const Common::String &fileName) {
+ // First, we need the resource fork for other reasons -- PanI resource
+ Common::MacResManager *resFork = new Common::MacResManager();
+ if (!resFork->open(fileName) || !resFork->hasResFork())
+ error("Could not open the resource fork of '%s'", fileName.c_str());
+
+ Common::SeekableReadStream *resource = resFork->getResource(kPanoramaResType, kPanoramaResID);
+ if (!resource)
+ error("No panorama information in the resource fork of '%s'", fileName.c_str());
+
+ _panoramaWidth = resource->readUint16BE();
+ _panoramaHeight = resource->readUint16BE();
+ _stripWidth = resource->readUint16BE();
+
+ delete resource;
+ delete resFork;
+
+ // Now we open the movie like normal
+ _panoramaMovie.initFromMovieFile(fileName);
+}
+
+void Panorama::setMask(Surface *mask) {
+ _mask = mask;
+}
+
+// If the panorama is not open, do nothing and return.
+// Otherwise, set up the view bounds.
+void Panorama::setViewBounds(const Common::Rect &newView) {
+ if (!isPanoramaOpen())
+ return;
+
+ if (newView.isEmpty())
+ return;
+
+ Common::Rect r = newView;
+
+ if (r.width() > _panoramaWidth) {
+ r.left = 0;
+ r.right = _panoramaWidth;
+ } else {
+ if (r.right > _panoramaWidth)
+ r.translate(_panoramaWidth - r.right, 0);
+
+ if (r.left < 0)
+ r.translate(-r.left, 0);
+ }
+
+ if (r.height() > _panoramaHeight) {
+ r.top = 0;
+ r.bottom = _panoramaHeight;
+ } else {
+ if (r.bottom > _panoramaHeight)
+ r.translate(0, _panoramaHeight - r.bottom);
+
+ if (r.top < 0)
+ r.translate(0, -r.top);
+ }
+
+ if (_viewBounds != r) {
+ CoordType stripLeft = 0;
+
+ if (r.width() != _viewBounds.width() || !_panoramaWorld.isSurfaceValid()) {
+ _panoramaWorld.deallocateSurface();
+ makeNewSurface(r);
+ } else {
+ CoordType stripRight;
+ calcStripRange(r, stripLeft, stripRight);
+ loadStrips(stripLeft, stripRight);
+ }
+
+ _viewBounds = r;
+ _drawBounds = r;
+ _drawBounds.translate(-stripLeft * _stripWidth, 0);
+ }
+}
+
+void Panorama::getViewBounds(Common::Rect &r) const {
+ r = _viewBounds;
+}
+
+void Panorama::getPanoramaBounds(Common::Rect &r) const {
+ r = Common::Rect(0, 0, _panoramaWidth, _panoramaHeight);
+}
+
+void Panorama::drawPanorama(const Common::Rect &destRect) {
+ if (_panoramaWorld.isSurfaceValid()) {
+ if (_mask)
+ _panoramaWorld.copyToCurrentPortMasked(_drawBounds, destRect, _mask);
+ else
+ _panoramaWorld.copyToCurrentPortTransparent(_drawBounds, destRect);
+ }
+}
+
+// Make a new Surface big enough to show r, which is assumed to be a valid view bounds.
+// Assumptions:
+// r is a valid view bounds.
+// _panoramaWorld is not allocated.
+// _panoramaHeight, _stripWidth is correct.
+// _panoramaMovie is allocated.
+void Panorama::makeNewSurface(const Common::Rect& view) {
+ CoordType stripLeft, stripRight;
+ calcStripRange(view, stripLeft, stripRight);
+
+ Common::Rect r(0, 0, (stripRight - stripLeft + 1) * _stripWidth, _panoramaHeight);
+ _panoramaWorld.allocateSurface(r);
+ _panoramaMovie.shareSurface(&_panoramaWorld);
+ loadStrips(stripLeft, stripRight);
+}
+
+// Assumes view is not empty.
+void Panorama::calcStripRange(const Common::Rect &view, CoordType &stripLeft, CoordType &stripRight) {
+ stripLeft = view.left / _stripWidth;
+ stripRight = (view.left - view.left % _stripWidth + _stripWidth - 1 + view.width()) / _stripWidth;
+}
+
+// Load in all needed strips to put range (stripLeft, stripRight) into the
+// panorama's Surface. Try to optimize by saving any pixels already in the Surface.
+// Assumptions:
+// Surface is allocated and is big enough for maximum range of
+// stripLeft and stripRight
+void Panorama::loadStrips(CoordType stripLeft, CoordType stripRight) {
+ if (_stripLeft == -1) {
+ // Surface has just been allocated.
+ // Load in all strips.
+ for (CoordType i = stripLeft; i <= stripRight; i++)
+ loadOneStrip(i, stripLeft);
+
+ _stripLeft = stripLeft;
+ _stripRight = stripRight;
+ } else if (stripLeft != _stripLeft) {
+ CoordType overlapLeft = MAX(stripLeft, _stripLeft);
+ CoordType overlapRight = MIN(stripRight, _stripRight);
+
+ if (overlapLeft <= overlapRight) {
+ Common::Rect r1((overlapLeft - _stripLeft) * _stripWidth, 0,
+ (overlapRight - _stripLeft + 1) * _stripWidth, _panoramaHeight);
+
+ if (stripLeft < _stripLeft) {
+ Common::Rect bounds;
+ _panoramaWorld.getSurfaceBounds(bounds);
+ _panoramaWorld.getSurface()->move(bounds.right - r1.right, 0, _panoramaHeight);
+
+ for (CoordType i = stripLeft; i < _stripLeft; i++)
+ loadOneStrip(i, stripLeft);
+ } else {
+ _panoramaWorld.getSurface()->move(-r1.left, 0, _panoramaHeight);
+
+ for (CoordType i = _stripRight + 1; i <= stripRight; i++)
+ loadOneStrip(i, stripLeft);
+ }
+ } else {
+ // No overlap.
+ // Load everything.
+ for (CoordType i = stripLeft; i <= stripRight; i++)
+ loadOneStrip(i, stripLeft);
+ }
+
+ _stripLeft = stripLeft;
+ _stripRight = stripRight;
+ } else if (stripRight > _stripRight) {
+ // Need to add one or more strips.
+ for (CoordType i = _stripRight + 1; i <= stripRight; i++)
+ loadOneStrip(i, _stripLeft);
+
+ _stripRight = stripRight;
+ } else if (stripRight < _stripRight) {
+ // Need to chop off one strip.
+ _stripRight = stripRight;
+ }
+}
+
+void Panorama::loadOneStrip(CoordType stripToLoad, CoordType leftStrip) {
+ _panoramaMovie.moveMovieBoxTo((stripToLoad - leftStrip) * _stripWidth, 0);
+ _panoramaMovie.setTime(stripToLoad, 1);
+ _panoramaMovie.redrawMovieWorld();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/alpha/panorama.h b/engines/pegasus/neighborhood/norad/alpha/panorama.h
new file mode 100644
index 0000000000..87c7b3bd4e
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/panorama.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H
+
+#include "pegasus/movie.h"
+
+namespace Pegasus {
+
+/*
+
+ Panorama implements a wide image using a specially constructed movie file.
+ The movie holds the image as a series of vertical strips, say 16 or 32 pixels wide.
+
+ The panorama bounds defines the entire panorama. The view bounds represents the
+ area on the panorama that is kept in memory.
+
+ The panorama bounds is also stored in the movie file; it cannot be changed. The
+ view bounds must always be a subset of the panorama bounds.
+
+ In actuality, the area kept in memory is at least as wide as the view bounds (but
+ may be wider to coincide with the width of the movies slices), and is as tall as
+ the panorama bounds. The view bounds is used by the drawPanorama function to draw
+ a piece of the panorama to the current screen.
+
+ The panorama movie is built at a time scale of 1, with each strip lasting for one
+ second, so that strip number corresponds exactly with the time value at which the
+ strip is stored.
+
+ TO USE:
+
+ Call one initFromMovieFile to open the movie. Then set up a view rect by
+ calling setViewBounds. Once these two functions have been called, drawPanorama
+ will draw the panorama.
+
+*/
+
+class Panorama {
+public:
+ Panorama();
+ virtual ~Panorama();
+
+ void initFromMovieFile(const Common::String &);
+ void releasePanorama();
+ bool isPanoramaOpen() { return _panoramaMovie.isMovieValid(); }
+
+ void setViewBounds(const Common::Rect &);
+ void getViewBounds(Common::Rect &) const;
+
+ void setMask(Surface *);
+
+ void getPanoramaBounds(Common::Rect &) const;
+
+ void drawPanorama(const Common::Rect &);
+
+protected:
+ void blankFields();
+ void makeNewSurface(const Common::Rect &);
+ void calcStripRange(const Common::Rect &, CoordType &, CoordType &);
+ void loadStrips(CoordType, CoordType);
+ void loadOneStrip(CoordType, CoordType);
+
+ Movie _panoramaMovie;
+ Surface _panoramaWorld, *_mask;
+ Common::Rect _viewBounds;
+ Common::Rect _drawBounds;
+ CoordType _panoramaWidth, _panoramaHeight;
+ CoordType _stripWidth; // Pixels per strip.
+ CoordType _numStrips;
+ CoordType _stripLeft, _stripRight;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp
new file mode 100644
index 0000000000..7865bbb442
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/neighborhood/norad/alpha/panoramascroll.h"
+
+namespace Pegasus {
+
+PanoramaScroll::PanoramaScroll(const DisplayElementID id) : IdlerAnimation(id) {
+ _boundsWidth = 0;
+ _totalWidth = 0;
+}
+
+void PanoramaScroll::initFromMovieFile(const Common::String &fileName) {
+ _panorama.initFromMovieFile(fileName);
+
+ Common::Rect r;
+ _panorama.getPanoramaBounds(r);
+ _totalWidth = r.width();
+}
+
+void PanoramaScroll::initMaskFromPICTFile(const Common::String &fileName) {
+ if (!_panorama.isPanoramaOpen())
+ return;
+
+ _mask.getImageFromPICTFile(fileName);
+ _panorama.setMask(&_mask);
+}
+
+void PanoramaScroll::releasePanorama() {
+ if (_panorama.isPanoramaOpen())
+ _panorama.releasePanorama();
+
+ _mask.deallocateSurface();
+}
+
+void PanoramaScroll::setBounds(const Common::Rect &r) {
+ Animation::setBounds(r);
+
+ _boundsWidth = r.width();
+
+ Common::Rect r2;
+ _panorama.getViewBounds(r2);
+ r2.right = r2.left + _boundsWidth;
+ r2.bottom = r2.top + r.height();
+ _panorama.setViewBounds(r2);
+}
+
+void PanoramaScroll::draw(const Common::Rect &) {
+ _panorama.drawPanorama(_bounds);
+}
+
+void PanoramaScroll::timeChanged(const TimeValue newTime) {
+ CoordType leftPixel = (_totalWidth - _boundsWidth) * newTime / getDuration();
+
+ Common::Rect r;
+ _panorama.getViewBounds(r);
+ if (leftPixel != r.left) {
+ _panorama.getViewBounds(r);
+ r.moveTo(leftPixel, 0);
+ _panorama.setViewBounds(r);
+ triggerRedraw();
+ }
+}
+
+void PanoramaScroll::getPanoramaBounds(Common::Rect &r) const {
+ _panorama.getPanoramaBounds(r);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h
new file mode 100644
index 0000000000..6a3e1515e2
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H
+
+#include "pegasus/neighborhood/norad/alpha/panorama.h"
+
+namespace Pegasus {
+
+class PanoramaScroll : public IdlerAnimation {
+public:
+ PanoramaScroll(const DisplayElementID);
+ virtual ~PanoramaScroll() {}
+
+ void initFromMovieFile(const Common::String &);
+ void initMaskFromPICTFile(const Common::String &);
+
+ void releasePanorama();
+
+ bool isPanoramaOpen() { return _panorama.isPanoramaOpen(); }
+ void getPanoramaBounds(Common::Rect &) const;
+
+ void setBounds(const Common::Rect&);
+
+ void draw(const Common::Rect &);
+
+protected:
+ void timeChanged(const TimeValue);
+
+ Panorama _panorama;
+ Surface _mask;
+ CoordType _totalWidth, _boundsWidth;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/constants.h b/engines/pegasus/neighborhood/norad/constants.h
new file mode 100644
index 0000000000..37c1769309
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/constants.h
@@ -0,0 +1,755 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H
+
+#include "pegasus/constants.h"
+
+namespace Pegasus {
+
+// Norad Alpha spot constants
+
+static const TimeValue kAlphaBumpIntoWallIn = 0;
+static const TimeValue kAlphaBumpIntoWallOut = 303;
+
+static const TimeValue kAlphaAccessDeniedIn = 303;
+static const TimeValue kAlphaAccessDeniedOut = 3045;
+
+static const TimeValue kAlphaRegDoorCloseIn = 3045;
+static const TimeValue kAlphaRegDoorCloseOut = 4476;
+
+static const TimeValue kAlphaElevatorDoorCloseIn = 4476;
+static const TimeValue kAlphaElevatorDoorCloseOut = 5071;
+
+static const TimeValue kAlphaCantTransportIn = 5071;
+static const TimeValue kAlphaCantTransportOut = 9348;
+
+static const TimeValue kAlphaPressureDoorIntro1In = 9348;
+static const TimeValue kAlphaPressureDoorIntro1Out = 11061;
+
+static const TimeValue kAlphaPressureDoorIntro2In = 11061;
+static const TimeValue kAlphaPressureDoorIntro2Out = 14098;
+
+static const TimeValue kN22ReplyIn = 14098;
+static const TimeValue kN22ReplyOut = 18442;
+
+static const TimeValue kAlphaLoadClawIntroIn = 18442;
+static const TimeValue kAlphaLoadClawIntroOut = 20698;
+
+// Norad Delta spot constants
+
+static const TimeValue kDeltaBumpIntoWallIn = 0;
+static const TimeValue kDeltaBumpIntoWallOut = 303;
+
+static const TimeValue kDeltaAccessDeniedIn = 303;
+static const TimeValue kDeltaAccessDeniedOut = 3045;
+
+static const TimeValue kDeltaRegDoorCloseIn = 3045;
+static const TimeValue kDeltaRegDoorCloseOut = 4476;
+
+static const TimeValue kDeltaElevatorDoorCloseIn = 4476;
+static const TimeValue kDeltaElevatorDoorCloseOut = 5071;
+
+static const TimeValue kPressureDoorIntro1In = 5071;
+static const TimeValue kPressureDoorIntro1Out = 6784;
+
+static const TimeValue kPressureDoorIntro2In = 6784;
+static const TimeValue kPressureDoorIntro2Out = 9821;
+
+static const TimeValue kLoadClawIntroIn = 9821;
+static const TimeValue kLoadClawIntroOut = 12077;
+
+static const TimeValue kHoldForRetinalIn = 12077;
+static const TimeValue kHoldForRetinalOut = 14104;
+
+static const TimeValue kRetinalScanFailedIn = 14104;
+static const TimeValue kRetinalScanFailedOut = 17538;
+
+static const TimeValue kAddisAbabaIn = 17538;
+static const TimeValue kAddisAbabaOut = 19263;
+
+static const TimeValue kBangkokIn = 19263;
+static const TimeValue kBangkokOut = 20201;
+
+static const TimeValue kBonnIn = 20201;
+static const TimeValue kBonnOut = 20915;
+
+static const TimeValue kDublinIn = 20915;
+static const TimeValue kDublinOut = 21660;
+
+static const TimeValue kHonoluluIn = 21660;
+static const TimeValue kHonoluluOut = 22498;
+
+static const TimeValue kMadridIn = 22498;
+static const TimeValue kMadridOut = 23474;
+
+static const TimeValue kReykjavikIn = 23474;
+static const TimeValue kReykjavikOut = 24488;
+
+static const TimeValue kSanAntonioIn = 24488;
+static const TimeValue kSanAntonioOut = 25561;
+
+static const TimeValue kSeoulIn = 25561;
+static const TimeValue kSeoulOut = 26461;
+
+static const TimeValue kSvortalskIn = 26461;
+static const TimeValue kSvortalskOut = 27582;
+
+static const TimeValue kSiloBeepIn = 27582;
+static const TimeValue kSiloBeepOut = 27721;
+
+static const TimeValue kAllSilosDeactivatedIn = 27721;
+static const TimeValue kAllSilosDeactivatedOut = 28928;
+
+static const TimeValue kGlobalLaunchOverrideIn = 28928;
+static const TimeValue kGlobalLaunchOverrideOut = 30736;
+
+static const TimeValue kLaunchSiloSelectedIn = 30736;
+static const TimeValue kLaunchSiloSelectedOut = 31660;
+
+static const TimeValue kLaunchToProceedIn = 31660;
+static const TimeValue kLaunchToProceedOut = 32536;
+
+static const TimeValue kMaximumDeactivationIn = 32536;
+static const TimeValue kMaximumDeactivationOut = 34337;
+
+static const TimeValue kMissileLaunchedIn = 34337;
+static const TimeValue kMissileLaunchedOut = 35082;
+
+static const TimeValue kNewLaunchSiloIn = 35082;
+static const TimeValue kNewLaunchSiloOut = 36320;
+
+static const TimeValue kStrikeAuthorizedIn = 36320;
+static const TimeValue kStrikeAuthorizedOut = 37393;
+
+static const TimeValue kPrimaryTargetIn = 37393;
+static const TimeValue kPrimaryTargetOut = 38628;
+
+static const TimeValue kSiloDeactivatedIn = 38628;
+static const TimeValue kSiloDeactivatedOut = 39566;
+
+static const TimeValue kStrikeCodeRejectedIn = 39566;
+static const TimeValue kStrikeCodeRejectedOut = 41056;
+
+static const TimeValue kToDeactivateIn = 41056;
+static const TimeValue kToDeactivateOut = 46494;
+
+static const TimeValue kTwoMinutesIn = 46494;
+static const TimeValue kTwoMinutesOut = 47166;
+
+static const TimeValue kOneMinuteIn = 47166;
+static const TimeValue kOneMinuteOut = 47856;
+
+static const TimeValue kFiftySecondsIn = 47856;
+static const TimeValue kFiftySecondsOut = 48691;
+
+static const TimeValue kFortySecondsIn = 48691;
+static const TimeValue kFortySecondsOut = 49500;
+
+static const TimeValue kThirtySecondsIn = 49500;
+static const TimeValue kThirtySecondsOut = 50362;
+
+static const TimeValue kTwentySecondsIn = 50362;
+static const TimeValue kTwentySecondsOut = 51245;
+
+static const TimeValue kTenSecondsIn = 51245;
+static const TimeValue kTenSecondsOut = 52069;
+
+static const TimeValue kGiveUpHumanIn = 52069;
+static const TimeValue kGiveUpHumanOut = 55023;
+
+static const TimeValue kIJustBrokeIn = 55023;
+static const TimeValue kIJustBrokeOut = 59191;
+
+static const TimeValue kTheOnlyGoodHumanIn = 59191;
+static const TimeValue kTheOnlyGoodHumanOut = 62379;
+
+static const TimeValue kYouAreRunningIn = 62379;
+static const TimeValue kYouAreRunningOut = 64201;
+
+static const TimeValue kYouCannotPossiblyIn = 64201;
+static const TimeValue kYouCannotPossiblyOut = 65740;
+
+static const TimeValue kYouWillFailIn = 65740;
+static const TimeValue kYouWillFailOut = 67217;
+
+static const CanOpenDoorReason kCantOpenBadPressure = kCantOpenLastReason + 1;
+
+static const NotificationFlags kAirTimerExpiredFlag = kLastNeighborhoodNotificationFlag << 1;
+
+static const uint16 kNoradWarningVolume = 0x100 / 3;
+static const uint16 kNoradSuckWindVolume = 0x100 / 2;
+
+static const int16 kElevatorCompassAngle = -40;
+static const int16 kSubPlatformCompassAngle = 45;
+static const int16 kSubControlCompassAngle = -10;
+
+// Norad interactions.
+
+static const InteractionID kNoradGlobeGameInteractionID = 0;
+static const InteractionID kNoradECRMonitorInteractionID = 1;
+static const InteractionID kNoradFillingStationInteractionID = 2;
+static const InteractionID kNoradElevatorInteractionID = 3;
+static const InteractionID kNoradPressureDoorInteractionID = 4;
+static const InteractionID kNoradSubControlRoomInteractionID = 5;
+static const InteractionID kNoradSubPlatformInteractionID = 6;
+
+/////////////////////////////////////////////
+//
+// Norad Alpha
+
+static const CoordType kECRSlideShowLeft = kNavAreaLeft + 78;
+static const CoordType kECRSlideShowTop = kNavAreaTop + 1;
+
+static const CoordType kECRPanLeft = kNavAreaLeft + 78 + 5;
+static const CoordType kECRPanTop = kNavAreaTop + 1 + 4;
+static const CoordType kECRPanRight = kECRPanLeft + 213;
+static const CoordType kECRPanBottom = kECRPanTop + 241;
+
+static const CoordType kNoradAlphaElevatorControlsLeft = kNavAreaLeft + 332;
+static const CoordType kNoradAlphaElevatorControlsTop = kNavAreaTop + 127;
+
+static const CoordType kNoradAlpha01LeftSideLeft = kNavAreaLeft + 0;
+static const CoordType kNoradAlpha01LeftSideTop = kNavAreaTop + 0;
+
+static const CoordType kNoradAlpha01RightSideLeft = kNavAreaLeft + 240;
+static const CoordType kNoradAlpha01RightSideTop = kNavAreaTop + 12;
+
+static const CoordType kNoradUpperLevelsLeft = kNavAreaLeft + 98;
+static const CoordType kNoradUpperLevelsTop = kNavAreaTop + 31;
+
+static const CoordType kNoradUpperTypeLeft = kNoradUpperLevelsLeft + 114;
+static const CoordType kNoradUpperTypeTop = kNoradUpperLevelsTop + 8;
+
+static const CoordType kNoradUpperUpLeft = kNavAreaLeft + 361;
+static const CoordType kNoradUpperUpTop = kNavAreaTop + 32;
+
+static const CoordType kNoradUpperDownLeft = kNavAreaLeft + 367;
+static const CoordType kNoradUpperDownTop = kNavAreaTop + 66;
+
+static const CoordType kNoradLowerLevelsLeft = kNavAreaLeft + 74;
+static const CoordType kNoradLowerLevelsTop = kNavAreaTop + 157;
+
+static const CoordType kNoradLowerTypeLeft = kNoradLowerLevelsLeft + 144;
+static const CoordType kNoradLowerTypeTop = kNoradLowerLevelsTop + 9;
+
+static const CoordType kNoradLowerUpLeft = kNavAreaLeft + 380;
+static const CoordType kNoradLowerUpTop = kNavAreaTop + 164;
+
+static const CoordType kNoradLowerDownLeft = kNavAreaLeft + 388;
+static const CoordType kNoradLowerDownTop = kNavAreaTop + 212;
+
+static const CoordType kNoradPlatformLeft = kNavAreaLeft + 36;
+static const CoordType kNoradPlatformTop = kNavAreaTop + 87;
+
+static const CoordType kNoradSubControlLeft = kNavAreaLeft + 0;
+static const CoordType kNoradSubControlTop = kNavAreaTop + 84;
+
+static const CoordType kNoradSubControlPinchLeft = kNoradSubControlLeft + 106;
+static const CoordType kNoradSubControlPinchTop = kNoradSubControlTop + 86;
+
+static const CoordType kNoradSubControlDownLeft = kNoradSubControlLeft + 66;
+static const CoordType kNoradSubControlDownTop = kNoradSubControlTop + 106;
+
+static const CoordType kNoradSubControlRightLeft = kNoradSubControlLeft + 83;
+static const CoordType kNoradSubControlRightTop = kNoradSubControlTop + 90;
+
+static const CoordType kNoradSubControlLeftLeft = kNoradSubControlLeft + 56;
+static const CoordType kNoradSubControlLeftTop = kNoradSubControlTop + 91;
+
+static const CoordType kNoradSubControlUpLeft = kNoradSubControlLeft + 66;
+static const CoordType kNoradSubControlUpTop = kNoradSubControlTop + 81;
+
+static const CoordType kNoradSubControlCCWLeft = kNoradSubControlLeft + 29;
+static const CoordType kNoradSubControlCCWTop = kNoradSubControlTop + 88;
+
+static const CoordType kNoradSubControlCWLeft = kNoradSubControlLeft + 0;
+static const CoordType kNoradSubControlCWTop = kNoradSubControlTop + 89;
+
+static const CoordType kNoradClawMonitorLeft = kNavAreaLeft + 288;
+static const CoordType kNoradClawMonitorTop = kNavAreaTop + 97;
+
+static const CoordType kNoradGreenBallAtALeft = kNoradClawMonitorLeft + 179;
+static const CoordType kNoradGreenBallAtATop = kNoradClawMonitorTop + 82;
+
+static const CoordType kNoradGreenBallAtBLeft = kNoradClawMonitorLeft + 130;
+static const CoordType kNoradGreenBallAtBTop = kNoradClawMonitorTop + 73;
+
+static const CoordType kNoradGreenBallAtCLeft = kNoradClawMonitorLeft + 110;
+static const CoordType kNoradGreenBallAtCTop = kNoradClawMonitorTop + 26;
+
+static const CoordType kNoradGreenBallAtDLeft = kNoradClawMonitorLeft + 21;
+static const CoordType kNoradGreenBallAtDTop = kNoradClawMonitorTop + 49;
+
+/////////////////////////////////////////////
+//
+// Norad Delta
+
+static const CoordType kGlobeMonitorLeft = kNavAreaLeft + 360;
+static const CoordType kGlobeMonitorTop = kNavAreaTop + 144;
+
+static const CoordType kGlobeLeft = kNavAreaLeft + 172;
+static const CoordType kGlobeTop = kNavAreaTop;
+
+static const CoordType kGlobeCircleLeftLeft = kNavAreaLeft + 186;
+static const CoordType kGlobeCircleLeftTop = kNavAreaTop + 41;
+
+static const CoordType kGlobeCircleRightLeft = kNavAreaLeft + 321;
+static const CoordType kGlobeCircleRightTop = kNavAreaTop + 41;
+
+static const CoordType kGlobeCircleUpLeft = kNavAreaLeft + 220;
+static const CoordType kGlobeCircleUpTop = kNavAreaTop + 7;
+
+static const CoordType kGlobeCircleDownLeft = kNavAreaLeft + 220;
+static const CoordType kGlobeCircleDownTop = kNavAreaTop + 142;
+
+static const CoordType kGlobeUpperLeftHiliteLeft = kNavAreaLeft + 207;
+static const CoordType kGlobeUpperLeftHiliteTop = kNavAreaTop + 28;
+
+static const CoordType kGlobeUpperRightHiliteLeft = kNavAreaLeft + 307;
+static const CoordType kGlobeUpperRightHiliteTop = kNavAreaTop + 28;
+
+static const CoordType kGlobeLowerLeftHiliteLeft = kNavAreaLeft + 207;
+static const CoordType kGlobeLowerLeftHiliteTop = kNavAreaTop + 128;
+
+static const CoordType kGlobeLowerRightHiliteLeft = kNavAreaLeft + 307;
+static const CoordType kGlobeLowerRightHiliteTop = kNavAreaTop + 128;
+
+static const CoordType kGlobeLeftMotionHiliteLeft = kNavAreaLeft + 182;
+static const CoordType kGlobeLeftMotionHiliteTop = kNavAreaTop + 60;
+
+static const CoordType kGlobeRightMotionHiliteLeft = kNavAreaLeft + 331;
+static const CoordType kGlobeRightMotionHiliteTop = kNavAreaTop + 60;
+
+static const CoordType kGlobeUpMotionHiliteLeft = kNavAreaLeft + 239;
+static const CoordType kGlobeUpMotionHiliteTop = kNavAreaTop + 3;
+
+static const CoordType kGlobeDownMotionHiliteLeft = kNavAreaLeft + 239;
+static const CoordType kGlobeDownMotionHiliteTop = kNavAreaTop + 152;
+
+static const CoordType kGlobeUpperNamesLeft = kNavAreaLeft + 368;
+static const CoordType kGlobeUpperNamesTop = kNavAreaTop + 188;
+
+static const CoordType kGlobeLowerNamesLeft = kNavAreaLeft + 368;
+static const CoordType kGlobeLowerNamesTop = kNavAreaTop + 212;
+
+static const CoordType kGlobeCountdownLeft = kNavAreaLeft + 478;
+static const CoordType kGlobeCountdownTop = kNavAreaTop + 164;
+
+// Norad Alpha display IDs.
+
+static const DisplayElementID kECRSlideShowMovieID = kNeighborhoodDisplayID;
+static const DisplayElementID kECRPanID = kECRSlideShowMovieID + 1;
+static const DisplayElementID kNoradAlphaDeathMovieID = kECRPanID + 1;
+static const DisplayElementID kNoradElevatorControlsID = kNoradAlphaDeathMovieID + 1;
+static const DisplayElementID kN01LeftSideID = kNoradElevatorControlsID + 1;
+static const DisplayElementID kN01RightSideID = kN01LeftSideID + 1;
+static const DisplayElementID kPressureDoorLevelsID = kN01RightSideID + 1;
+static const DisplayElementID kPressureDoorTypeID = kPressureDoorLevelsID + 1;
+static const DisplayElementID kPressureDoorUpButtonID = kPressureDoorTypeID + 1;
+static const DisplayElementID kPressureDoorDownButtonID = kPressureDoorUpButtonID + 1;
+static const DisplayElementID kPlatformMonitorID = kPressureDoorDownButtonID + 1;
+static const DisplayElementID kSubControlMonitorID = kPlatformMonitorID + 1;
+static const DisplayElementID kClawMonitorID = kSubControlMonitorID + 1;
+static const DisplayElementID kSubControlPinchID = kClawMonitorID + 1;
+static const DisplayElementID kSubControlDownID = kSubControlPinchID + 1;
+static const DisplayElementID kSubControlRightID = kSubControlDownID + 1;
+static const DisplayElementID kSubControlLeftID = kSubControlRightID + 1;
+static const DisplayElementID kSubControlUpID = kSubControlLeftID + 1;
+static const DisplayElementID kSubControlCCWID = kSubControlUpID + 1;
+static const DisplayElementID kSubControlCWID = kSubControlCCWID + 1;
+static const DisplayElementID kClawMonitorGreenBallID = kSubControlCWID + 1;
+
+// Norad Delta display IDs.
+
+static const DisplayElementID kGlobeMonitorID = kNeighborhoodDisplayID;
+static const DisplayElementID kGlobeMovieID = kGlobeMonitorID + 14;
+static const DisplayElementID kGlobeCircleLeftID = kGlobeMovieID + 1;
+static const DisplayElementID kGlobeCircleRightID = kGlobeCircleLeftID + 1;
+static const DisplayElementID kGlobeCircleUpID = kGlobeCircleRightID + 1;
+static const DisplayElementID kGlobeCircleDownID = kGlobeCircleUpID + 1;
+static const DisplayElementID kMotionHiliteLeftID = kGlobeCircleDownID + 1;
+static const DisplayElementID kMotionHiliteRightID = kMotionHiliteLeftID + 1;
+static const DisplayElementID kMotionHiliteUpID = kMotionHiliteRightID + 1;
+static const DisplayElementID kMotionHiliteDownID = kMotionHiliteUpID + 1;
+static const DisplayElementID kTargetHiliteUpperLeftID = kMotionHiliteDownID + 1;
+static const DisplayElementID kTargetHiliteUpperRightID = kTargetHiliteUpperLeftID + 1;
+static const DisplayElementID kTargetHiliteLowerLeftID = kTargetHiliteUpperRightID + 1;
+static const DisplayElementID kTargetHiliteLowerRightID = kTargetHiliteLowerLeftID + 1;
+static const DisplayElementID kGlobeUpperNamesID = kTargetHiliteLowerRightID + 1;
+static const DisplayElementID kGlobeLowerNamesID = kGlobeUpperNamesID + 1;
+static const DisplayElementID kGlobeCountdownID = kGlobeLowerNamesID + 1;
+
+// Norad Alpha:
+
+static const DisplayOrder kECRMonitorOrder = kMonitorLayer;
+static const DisplayOrder kECRPanOrder = kECRMonitorOrder + 1;
+
+static const DisplayOrder kN01LeftSideOrder = kMonitorLayer;
+static const DisplayOrder kN01RightSideOrder = kN01LeftSideOrder + 1;
+
+static const DisplayOrder kElevatorControlsOrder = kMonitorLayer;
+
+static const DisplayOrder kPressureLevelsOrder = kMonitorLayer;
+static const DisplayOrder kPressureTypeOrder = kPressureLevelsOrder + 1;
+static const DisplayOrder kPressureUpOrder = kPressureTypeOrder + 1;
+static const DisplayOrder kPressureDownOrder = kPressureUpOrder + 1;
+
+static const DisplayOrder kPlatformOrder = kMonitorLayer;
+
+static const DisplayOrder kSubControlOrder = kMonitorLayer;
+static const DisplayOrder kClawMonitorOrder = kSubControlOrder + 1;
+static const DisplayOrder kSubControlPinchOrder = kClawMonitorOrder + 1;
+static const DisplayOrder kSubControlDownOrder = kSubControlPinchOrder + 1;
+static const DisplayOrder kSubControlRightOrder = kSubControlDownOrder + 1;
+static const DisplayOrder kSubControlLeftOrder = kSubControlRightOrder + 1;
+static const DisplayOrder kSubControlUpOrder = kSubControlLeftOrder + 1;
+static const DisplayOrder kSubControlCCWOrder = kSubControlUpOrder + 1;
+static const DisplayOrder kSubControlCWOrder = kSubControlCCWOrder + 1;
+static const DisplayOrder kClawMonitorGreenBallOrder = kSubControlCWOrder + 1;
+
+// Norad Delta:
+
+static const DisplayOrder kGlobeMonitorLayer = kMonitorLayer;
+static const DisplayOrder kGlobeMovieLayer = kGlobeMonitorLayer + 1;
+static const DisplayOrder kGlobeCircleLayer = kGlobeMovieLayer + 1;
+static const DisplayOrder kGlobeHilitesLayer = kGlobeCircleLayer + 1;
+static const DisplayOrder kGlobeUpperNamesLayer = kGlobeHilitesLayer + 1;
+static const DisplayOrder kGlobeLowerNamesLayer = kGlobeUpperNamesLayer + 1;
+static const DisplayOrder kGlobeCountdownLayer = kGlobeLowerNamesLayer + 1;
+
+// Norad Alpha Tables
+
+static const TimeScale kNoradAlphaMovieScale = 600;
+static const TimeScale kNoradAlphaFramesPerSecond = 15;
+static const TimeScale kNoradAlphaFrameDuration = 40;
+
+// Alternate IDs.
+
+static const AlternateID kAltNoradAlphaNormal = 0;
+
+// Room IDs.
+
+static const RoomID kNorad01 = 0;
+static const RoomID kNorad01East = 1;
+static const RoomID kNorad01West = 2;
+static const RoomID kNorad02 = 3;
+static const RoomID kNorad03 = 4;
+static const RoomID kNorad04 = 5;
+static const RoomID kNorad05 = 6;
+static const RoomID kNorad06 = 7;
+static const RoomID kNorad07 = 8;
+static const RoomID kNorad07North = 9;
+static const RoomID kNorad08 = 10;
+static const RoomID kNorad09 = 11;
+static const RoomID kNorad10 = 12;
+static const RoomID kNorad10East = 13;
+static const RoomID kNorad11 = 14;
+static const RoomID kNorad11South = 15;
+static const RoomID kNorad12 = 16;
+static const RoomID kNorad12South = 17;
+static const RoomID kNorad13 = 18;
+static const RoomID kNorad14 = 19;
+static const RoomID kNorad15 = 20;
+static const RoomID kNorad16 = 21;
+static const RoomID kNorad17 = 22;
+static const RoomID kNorad18 = 23;
+static const RoomID kNorad19 = 24;
+static const RoomID kNorad19West = 25;
+static const RoomID kNorad21 = 26;
+static const RoomID kNorad21West = 27;
+static const RoomID kNorad22 = 28;
+static const RoomID kNorad22West = 29;
+
+// Hot Spot Activation IDs.
+
+
+// Hot Spot IDs.
+
+static const HotSpotID kNorad01ECRSpotID = 5000;
+static const HotSpotID kNorad01GasSpotID = 5001;
+static const HotSpotID kNorad01ECROutSpotID = 5002;
+static const HotSpotID kNorad01GasOutSpotID = 5003;
+static const HotSpotID kNorad01MonitorSpotID = 5004;
+static const HotSpotID kNorad01IntakeSpotID = 5005;
+static const HotSpotID kNorad01DispenseSpotID = 5006;
+static const HotSpotID kNorad01ArSpotID = 5007;
+static const HotSpotID kNorad01CO2SpotID = 5008;
+static const HotSpotID kNorad01HeSpotID = 5009;
+static const HotSpotID kNorad01OSpotID = 5010;
+static const HotSpotID kNorad01NSpotID = 5011;
+static const HotSpotID kN01GasCanisterSpotID = 5012;
+static const HotSpotID kN01ArgonCanisterSpotID = 5013;
+static const HotSpotID kN01AirMaskSpotID = 5014;
+static const HotSpotID kN01NitrogenCanisterSpotID = 5015;
+static const HotSpotID kN01GasOutletSpotID = 5016;
+static const HotSpotID kNorad07DoorSpotID = 5017;
+static const HotSpotID kNorad07DoorOutSpotID = 5018;
+static const HotSpotID kNorad10DoorSpotID = 5019;
+static const HotSpotID kNorad10EastOutSpotID = 5020;
+static const HotSpotID kAlphaUpperPressureDoorUpSpotID = 5021;
+static const HotSpotID kAlphaUpperPressureDoorDownSpotID = 5022;
+static const HotSpotID kNorad11ElevatorSpotID = 5023;
+static const HotSpotID kNorad11ElevatorOutSpotID = 5024;
+static const HotSpotID kNorad11ElevatorDownSpotID = 5025;
+static const HotSpotID kNorad12ElevatorSpotID = 5026;
+static const HotSpotID kNorad12ElevatorOutSpotID = 5027;
+static const HotSpotID kNorad12ElevatorUpSpotID = 5028;
+static const HotSpotID kNorad19MonitorSpotID = 5029;
+static const HotSpotID kNorad19MonitorOutSpotID = 5030;
+static const HotSpotID kNorad19ActivateMonitorSpotID = 5031;
+static const HotSpotID kNorad21WestSpotID = 5032;
+static const HotSpotID kNorad21WestOutSpotID = 5033;
+static const HotSpotID kAlphaLowerPressureDoorUpSpotID = 5034;
+static const HotSpotID kAlphaLowerPressureDoorDownSpotID = 5035;
+static const HotSpotID kNorad22MonitorSpotID = 5036;
+static const HotSpotID kNorad22MonitorOutSpotID = 5037;
+static const HotSpotID kNorad22LaunchPrepSpotID = 5038;
+static const HotSpotID kNorad22ClawControlSpotID = 5039;
+static const HotSpotID kNorad22ClawPinchSpotID = 5040;
+static const HotSpotID kNorad22ClawDownSpotID = 5041;
+static const HotSpotID kNorad22ClawRightSpotID = 5042;
+static const HotSpotID kNorad22ClawLeftSpotID = 5043;
+static const HotSpotID kNorad22ClawUpSpotID = 5044;
+static const HotSpotID kNorad22ClawCCWSpotID = 5045;
+static const HotSpotID kNorad22ClawCWSpotID = 5046;
+
+// Extra sequence IDs.
+
+static const ExtraID kNoradArriveFromTSA = 0;
+static const ExtraID kNorad01RobotTaunt = 1;
+static const ExtraID kNorad01ZoomInWithGasCanister = 2;
+static const ExtraID kN01WGasCanister = 3;
+static const ExtraID kNorad01ZoomOutWithGasCanister = 4;
+static const ExtraID kN01WZEmptyLit = 5;
+static const ExtraID kN01WZGasCanisterDim = 6;
+static const ExtraID kN01WZGasCanisterLit = 7;
+static const ExtraID kN01WZArgonCanisterDim = 8;
+static const ExtraID kN01WZArgonCanisterLit = 9;
+static const ExtraID kN01WZAirMaskDim = 10;
+static const ExtraID kN01WZAirMaskLit = 11;
+static const ExtraID kN01WZNitrogenCanisterDim = 12;
+static const ExtraID kN01WZNitrogenCanisterLit = 13;
+static const ExtraID kNorad04EastDeath = 14;
+static const ExtraID kNorad19PrepSub = 15;
+static const ExtraID kNorad19ExitToSub = 16;
+static const ExtraID kNorad22SouthIntro = 17;
+static const ExtraID kNorad22SouthReply = 18;
+static const ExtraID kNorad22SouthFinish = 19;
+static const ExtraID kN22ClawFromAToB = 20;
+static const ExtraID kN22ClawALoop = 21;
+static const ExtraID kN22ClawAPinch = 22;
+static const ExtraID kN22ClawACounterclockwise = 23;
+static const ExtraID kN22ClawAClockwise = 24;
+static const ExtraID kN22ClawFromBToA = 25;
+static const ExtraID kN22ClawFromBToC = 26;
+static const ExtraID kN22ClawFromBToD = 27;
+static const ExtraID kN22ClawBLoop = 28;
+static const ExtraID kN22ClawBPinch = 29;
+static const ExtraID kN22ClawBCounterclockwise = 30;
+static const ExtraID kN22ClawBClockwise = 31;
+static const ExtraID kN22ClawFromCToB = 32;
+static const ExtraID kN22ClawCLoop = 33;
+static const ExtraID kN22ClawCPinch = 34;
+static const ExtraID kN22ClawCCounterclockwise = 35;
+static const ExtraID kN22ClawCClockwise = 36;
+static const ExtraID kN22ClawFromDToB = 37;
+static const ExtraID kN22ClawDLoop = 38;
+static const ExtraID kN22ClawDPinch = 39;
+static const ExtraID kN22ClawDCounterclockwise = 40;
+static const ExtraID kN22ClawDClockwise = 41;
+
+
+// Norad Delta Extra sequence IDs.
+
+static const ExtraID kArriveFromSubChase = 0;
+static const ExtraID kN59ZoomWithRobot = 1;
+static const ExtraID kN59RobotApproaches = 2;
+static const ExtraID kN59RobotPunchLoop = 3;
+static const ExtraID kN59PlayerWins1 = 4;
+static const ExtraID kN59PlayerWins2 = 5;
+static const ExtraID kN59RobotWins = 6;
+static const ExtraID kN59RobotHeadOpens = 7;
+static const ExtraID kN59Biochips111 = 8;
+static const ExtraID kN59Biochips011 = 9;
+static const ExtraID kN59Biochips101 = 10;
+static const ExtraID kN59Biochips001 = 11;
+static const ExtraID kN59Biochips110 = 12;
+static const ExtraID kN59Biochips010 = 13;
+static const ExtraID kN59Biochips100 = 14;
+static const ExtraID kN59Biochips000 = 15;
+static const ExtraID kN59RobotDisappears = 16;
+static const ExtraID kN60ClawFromAToB = 17;
+static const ExtraID kN60ClawALoop = 18;
+static const ExtraID kN60ClawAPinch = 19;
+static const ExtraID kN60ClawACounterclockwise = 20;
+static const ExtraID kN60ClawAClockwise = 21;
+static const ExtraID kN60ClawFromBToA = 22;
+static const ExtraID kN60ClawFromBToC = 23;
+static const ExtraID kN60ClawFromBToD = 24;
+static const ExtraID kN60ClawBLoop = 25;
+static const ExtraID kN60ClawBPinch = 26;
+static const ExtraID kN60ClawBCounterclockwise = 27;
+static const ExtraID kN60ClawBClockwise = 28;
+static const ExtraID kN60ClawFromCToB = 29;
+static const ExtraID kN60ClawCLoop = 30;
+static const ExtraID kN60ClawCPinch = 31;
+static const ExtraID kN60ClawCCounterclockwise = 32;
+static const ExtraID kN60ClawCClockwise = 33;
+static const ExtraID kN60ClawFromDToB = 34;
+static const ExtraID kN60ClawDLoop = 35;
+static const ExtraID kN60ClawDPinch = 36;
+static const ExtraID kN60ClawDCounterclockwise = 37;
+static const ExtraID kN60ClawDClockwise = 38;
+static const ExtraID kN60RobotApproaches = 39;
+static const ExtraID kN60FirstMistake = 40;
+static const ExtraID kN60ArmActivated = 41;
+static const ExtraID kN60SecondMistake = 42;
+static const ExtraID kN60ArmToPositionB = 43;
+static const ExtraID kN60ThirdMistake = 44;
+static const ExtraID kN60ArmGrabsRobot = 45;
+static const ExtraID kN60FourthMistake = 46;
+static const ExtraID kN60ArmCarriesRobotToPositionA = 47;
+static const ExtraID kN60PlayerFollowsRobotToDoor = 48;
+static const ExtraID kN60RobotHeadOpens = 49;
+static const ExtraID kN60Biochips111 = 50;
+static const ExtraID kN60Biochips011 = 51;
+static const ExtraID kN60Biochips101 = 52;
+static const ExtraID kN60Biochips001 = 53;
+static const ExtraID kN60Biochips110 = 54;
+static const ExtraID kN60Biochips010 = 55;
+static const ExtraID kN60Biochips100 = 56;
+static const ExtraID kN60Biochips000 = 57;
+static const ExtraID kN60RobotDisappears = 58;
+static const ExtraID kNoradDeltaRetinalScanBad = 59;
+static const ExtraID kNoradDeltaRetinalScanGood = 60;
+static const ExtraID kN79BrightView = 61;
+
+// Norad Delta Tables
+
+static const TimeScale kNoradDeltaMovieScale = 600;
+static const TimeScale kNoradDeltaFramesPerSecond = 15;
+static const TimeScale kNoradDeltaFrameDuration = 40;
+
+// Alternate IDs.
+
+static const AlternateID kAltNoradDeltaNormal = 0;
+
+// Room IDs.
+
+static const RoomID kNorad41 = 0;
+static const RoomID kNorad42 = 1;
+static const RoomID kNorad43 = 2;
+static const RoomID kNorad44 = 3;
+static const RoomID kNorad45 = 4;
+static const RoomID kNorad46 = 5;
+static const RoomID kNorad47 = 6;
+static const RoomID kNorad48 = 7;
+static const RoomID kNorad48South = 8;
+static const RoomID kNorad49 = 9;
+static const RoomID kNorad49South = 10;
+static const RoomID kNorad50 = 11;
+static const RoomID kNorad50East = 12;
+static const RoomID kNorad51 = 13;
+static const RoomID kNorad52 = 14;
+static const RoomID kNorad53 = 15;
+static const RoomID kNorad54 = 16;
+static const RoomID kNorad54North = 17;
+static const RoomID kNorad55 = 18;
+static const RoomID kNorad56 = 19;
+static const RoomID kNorad57 = 20;
+static const RoomID kNorad58 = 21;
+static const RoomID kNorad59 = 22;
+static const RoomID kNorad59West = 23;
+static const RoomID kNorad60 = 24;
+static const RoomID kNorad60West = 25;
+static const RoomID kNorad61 = 26;
+static const RoomID kNorad62 = 27;
+static const RoomID kNorad63 = 28;
+static const RoomID kNorad64 = 29;
+static const RoomID kNorad65 = 30;
+static const RoomID kNorad66 = 31;
+static const RoomID kNorad67 = 32;
+static const RoomID kNorad68 = 33;
+static const RoomID kNorad68West = 34;
+static const RoomID kNorad69 = 35;
+static const RoomID kNorad78 = 36;
+static const RoomID kNorad79 = 37;
+static const RoomID kNorad79West = 38;
+
+// Hot Spot Activation IDs.
+
+
+// Hot Spot IDs.
+
+static const HotSpotID kNorad48ElevatorSpotID = 5000;
+static const HotSpotID kNorad48ElevatorOutSpotID = 5001;
+static const HotSpotID kNorad48ElevatorUpSpotID = 5002;
+static const HotSpotID kNorad49ElevatorSpotID = 5003;
+static const HotSpotID kNorad49ElevatorOutSpotID = 5004;
+static const HotSpotID kNorad49ElevatorDownSpotID = 5005;
+static const HotSpotID kNorad50DoorSpotID = 5006;
+static const HotSpotID kNorad50DoorOutSpotID = 5007;
+static const HotSpotID kDeltaUpperPressureDoorUpSpotID = 5008;
+static const HotSpotID kDeltaUpperPressureDoorDownSpotID = 5009;
+static const HotSpotID kNorad54DoorSpotID = 5010;
+static const HotSpotID kNorad54DoorOutSpotID = 5011;
+static const HotSpotID kNorad59WestSpotID = 5012;
+static const HotSpotID kNorad59WestOutSpotID = 5013;
+static const HotSpotID kDeltaLowerPressureDoorUpSpotID = 5014;
+static const HotSpotID kDeltaLowerPressureDoorDownSpotID = 5015;
+static const HotSpotID kDelta59RobotHeadSpotID = 5016;
+static const HotSpotID kDelta59RobotShieldBiochipSpotID = 5017;
+static const HotSpotID kDelta59RobotOpMemBiochipSpotID = 5018;
+static const HotSpotID kDelta59RobotRetinalBiochipSpotID = 5019;
+static const HotSpotID kNorad60MonitorSpotID = 5020;
+static const HotSpotID kNorad60MonitorOutSpotID = 5021;
+static const HotSpotID kNorad60LaunchPrepSpotID = 5022;
+static const HotSpotID kNorad60ClawControlSpotID = 5023;
+static const HotSpotID kNorad60ClawPinchSpotID = 5024;
+static const HotSpotID kNorad60ClawDownSpotID = 5025;
+static const HotSpotID kNorad60ClawRightSpotID = 5026;
+static const HotSpotID kNorad60ClawLeftSpotID = 5027;
+static const HotSpotID kNorad60ClawUpSpotID = 5028;
+static const HotSpotID kNorad60ClawCCWSpotID = 5029;
+static const HotSpotID kNorad60ClawCWSpotID = 5030;
+static const HotSpotID kDelta60RobotHeadSpotID = 5031;
+static const HotSpotID kDelta60RobotShieldBiochipSpotID = 5032;
+static const HotSpotID kDelta60RobotOpMemBiochipSpotID = 5033;
+static const HotSpotID kDelta60RobotRetinalBiochipSpotID = 5034;
+static const HotSpotID kNorad68WestSpotID = 5035;
+static const HotSpotID kNorad68WestOutSpotID = 5036;
+static const HotSpotID kNorad79WestSpotID = 5037;
+static const HotSpotID kNorad79WestOutSpotID = 5038;
+static const HotSpotID kNorad79SpinLeftSpotID = 5039;
+static const HotSpotID kNorad79SpinRightSpotID = 5040;
+static const HotSpotID kNorad79SpinUpSpotID = 5041;
+static const HotSpotID kNorad79SpinDownSpotID = 5042;
+static const HotSpotID kNorad79SiloAreaSpotID = 5043;
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.cpp b/engines/pegasus/neighborhood/norad/delta/globegame.cpp
new file mode 100644
index 0000000000..06e40c2b3a
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/delta/globegame.cpp
@@ -0,0 +1,1062 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/cursor.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/delta/globegame.h"
+#include "pegasus/neighborhood/norad/delta/noraddelta.h"
+
+namespace Pegasus {
+
+static const TimeValue kDurationPerFrame = 600 / 15;
+static const TimeValue kDurationPerRow = kNumLongSlices * kDurationPerFrame;
+static const short kVerticalDuration = 16;
+
+GlobeTracker::GlobeTracker(Movie *globeMovie, Picture *leftHighlight, Picture *rightHighlight,
+ Picture *upHighlight, Picture *downHighlight) {
+ _globeMovie = globeMovie;
+ _leftHighlight = leftHighlight;
+ _rightHighlight = rightHighlight;
+ _upHighlight = upHighlight;
+ _downHighlight = downHighlight;
+}
+
+void GlobeTracker::setTrackParameters(const Hotspot *trackSpot, GlobeTrackDirection direction) {
+ _trackSpot = trackSpot;
+ _trackDirection = direction;
+
+ TimeValue time, newTime, start;
+
+ switch (_trackDirection) {
+ case kTrackLeft:
+ time = _globeMovie->getTime();
+
+ if (((time / kDurationPerRow) & 1) == 0) {
+ start = (time / kDurationPerRow + 1) * kDurationPerRow;
+ newTime = start + kDurationPerRow - time % kDurationPerRow;
+ } else {
+ start = (time / kDurationPerRow) * kDurationPerRow;
+ newTime = time;
+ }
+
+ _globeMovie->setSegment(start, start + kDurationPerRow);
+
+ if (newTime != time) {
+ _globeMovie->setTime(newTime);
+ _globeMovie->redrawMovieWorld();
+ }
+
+ _globeMovie->setFlags(kLoopTimeBase);
+ break;
+ case kTrackRight:
+ time = _globeMovie->getTime();
+
+ if (((time / kDurationPerRow) & 1) == 0) {
+ start = (time / kDurationPerRow) * kDurationPerRow;
+ newTime = time;
+ } else {
+ start = (time / kDurationPerRow - 1) * kDurationPerRow;
+ newTime = start + kDurationPerRow - time % kDurationPerRow;
+ }
+
+ _globeMovie->setSegment(start, start + kDurationPerRow);
+
+ if (newTime != time) {
+ _globeMovie->setTime(newTime);
+ _globeMovie->redrawMovieWorld();
+ }
+
+ _globeMovie->setFlags(kLoopTimeBase);
+ break;
+ case kTrackUp:
+ case kTrackDown:
+ _globeMovie->setSegment(0, _globeMovie->getDuration());
+ _globeMovie->setFlags(0);
+ break;
+ }
+}
+
+void GlobeTracker::activateHotspots() {
+ Tracker::activateHotspots();
+
+ if (_trackSpot)
+ g_allHotspots.activateOneHotspot(_trackSpot->getObjectID());
+}
+
+bool GlobeTracker::stopTrackingInput(const Input &input) {
+ return !JMPPPInput::isPressingInput(input);
+}
+
+void GlobeTracker::continueTracking(const Input &input) {
+ Common::Point where;
+ input.getInputLocation(where);
+
+ if (g_allHotspots.findHotspot(where) == _trackSpot)
+ trackGlobeMovie();
+ else
+ stopGlobeMovie();
+}
+
+void GlobeTracker::startTracking(const Input &input) {
+ Tracker::startTracking(input);
+ trackGlobeMovie();
+}
+
+void GlobeTracker::stopTracking(const Input &input) {
+ Tracker::stopTracking(input);
+ stopGlobeMovie();
+}
+
+void GlobeTracker::trackGlobeMovie() {
+ TimeValue time;
+
+ switch (_trackDirection) {
+ case kTrackLeft:
+ if (!_globeMovie->isRunning())
+ _globeMovie->start();
+
+ _leftHighlight->show();
+ break;
+ case kTrackRight:
+ if (!_globeMovie->isRunning())
+ _globeMovie->start();
+
+ _rightHighlight->show();
+ break;
+ case kTrackUp:
+ time = _globeMovie->getTime();
+
+ if (_trackTime == 0) {
+ _trackTime = tickCount();
+ } else if ((int)time - (int)kDurationPerRow * 2 >= 0 && (int)tickCount() >= _trackTime + kVerticalDuration) {
+ _trackTime = tickCount();
+ _globeMovie->setTime(time - kDurationPerRow * 2);
+ _globeMovie->redrawMovieWorld();
+ }
+
+ _upHighlight->show();
+ break;
+ case kTrackDown:
+ time = _globeMovie->getTime();
+
+ if (_trackTime == 0) {
+ _trackTime = tickCount();
+ } else if (time + kDurationPerRow * 2 < _globeMovie->getDuration() && (int)tickCount() >= _trackTime + kVerticalDuration) {
+ _trackTime = tickCount();
+ _globeMovie->setTime(time + kDurationPerRow * 2);
+ _globeMovie->redrawMovieWorld();
+ }
+
+ _downHighlight->show();
+ break;
+ }
+}
+
+void GlobeTracker::stopGlobeMovie() {
+ switch (_trackDirection) {
+ case kTrackLeft:
+ _leftHighlight->hide();
+ _globeMovie->stop();
+ break;
+ case kTrackRight:
+ _rightHighlight->hide();
+ _globeMovie->stop();
+ break;
+ case kTrackUp:
+ _upHighlight->hide();
+ _trackTime = tickCount() - kVerticalDuration;
+ break;
+ case kTrackDown:
+ _downHighlight->hide();
+ _trackTime = tickCount() - kVerticalDuration;
+ break;
+ }
+}
+
+// Globe game PICTs:
+static const ResIDType kGlobeCircleLeftPICTID = 300;
+static const ResIDType kGlobeCircleRightPICTID = 301;
+static const ResIDType kGlobeCircleUpPICTID = 302;
+static const ResIDType kGlobeCircleDownPICTID = 303;
+static const ResIDType kTargetUpperLeftPICTID = 304;
+static const ResIDType kTargetUpperRightPICTID = 305;
+static const ResIDType kTargetLowerLeftPICTID = 306;
+static const ResIDType kTargetLowerRightPICTID = 307;
+static const ResIDType kMotionHiliteLeftPICTID = 308;
+static const ResIDType kMotionHiliteRightPICTID = 309;
+static const ResIDType kMotionHiliteUpPICTID = 310;
+static const ResIDType kMotionHiliteDownPICTID = 311;
+
+static const ResIDType kGlobeCountdownDigitsID = 350;
+
+static const int kGlobeCountdownWidth = 28;
+static const int kGlobeCountdownHeight = 12;
+static const int kGlobeCountdownOffset1 = 12;
+static const int kGlobeCountdownOffset2 = 20;
+
+GlobeCountdown::GlobeCountdown(const DisplayElementID id) : IdlerAnimation(id) {
+ _digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCountdownDigitsID);
+
+ Common::Rect r;
+ _digits.getSurfaceBounds(r);
+ _digitOffset = r.width() / 10;
+ setScale(1);
+ sizeElement(kGlobeCountdownWidth, kGlobeCountdownHeight);
+}
+
+void GlobeCountdown::setDisplayOrder(const DisplayOrder order) {
+ IdlerAnimation::setDisplayOrder(order);
+}
+
+void GlobeCountdown::show() {
+ IdlerAnimation::show();
+}
+
+void GlobeCountdown::hide() {
+ IdlerAnimation::hide();
+}
+
+void GlobeCountdown::moveElementTo(const CoordType x, const CoordType y) {
+ IdlerAnimation::moveElementTo(x, y);
+}
+
+void GlobeCountdown::setCountdownTime(const int numSeconds) {
+ stop();
+ setSegment(0, numSeconds);
+ setTime(numSeconds);
+}
+
+void GlobeCountdown::startCountdown() {
+ setRate(-1);
+}
+
+void GlobeCountdown::stopCountdown() {
+ stop();
+}
+
+void GlobeCountdown::draw(const Common::Rect &) {
+ Common::Rect r1;
+ _digits.getSurfaceBounds(r1);
+ r1.right = r1.left + _digitOffset;
+ Common::Rect r2 = r1;
+ TimeValue time = getTime();
+
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ if (time > 60 * 9 + 59) {
+ r2.moveTo(bounds.left, bounds.top);
+ r1.moveTo(9 * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+
+ r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top);
+ r1.moveTo(5 * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+
+ r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top);
+ r1.moveTo(9 * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+ } else {
+ r2.moveTo(bounds.left, bounds.top);
+ r1.moveTo((time / 60) * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+
+ time %= 60;
+ r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top);
+ r1.moveTo((time / 10) * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+
+ r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top);
+ r1.moveTo((time % 10) * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+ }
+}
+
+const int16 GlobeGame::_siloCoords[kNumAllSilos][2] = {
+ { 60, -151 }, // Anchorage, Alaska
+ { 6, 39 }, // Addis Ababa, Ethiopia
+ { -22, 44 }, // Antaro, Madagascar
+ { 30, -83 }, // Atlanta, Georgia
+ { -41, 173 }, // Auckland, New Zealand
+ { 39, -78 }, // Baltimore, Maryland
+ { 11, 101 }, // Bangkok, Thailand
+ { 2, -75 }, // Bogota, Colombia
+ { 46, 4 }, // Bonn, Germany
+ { 51, -7 }, // Dublin, Ireland
+ { 28, -1 }, // El Menia, Algeria
+ { 67, -111 }, // Ellesmere, Canada
+ { 43, -107 }, // Glasgow, Montana
+ { 61, -48 }, // Godthab, Greenland
+ { 19, -157 }, // Honolulu, Hawaii
+ { 6, 5 }, // Ibadan, Nigeria
+ { -29, 26 }, // Johannesburg, South Africa
+ { 46, 92 }, // Kobdo, Mongolia
+ { -15, -63 }, // La Paz, Bolivia
+ { -35, -61 }, // La Plata, Argentina
+ { -9, -76 }, // Lima, Peru
+ { 38, -4 }, // Madrid, Spain
+ { -8, -51 }, // Manaus, Brazil
+ { 13, 120 }, // Manila, Phillipines
+ { -35, 143 }, // Melbourne, Australia
+ { 60, -161 }, // Nome, Alaska
+ { -7, 142 }, // Papua, New Guinea
+ { -32, 117 }, // Perth, West Australia
+ { 34, -114 }, // Phoenix, Arizona
+ { 18, -71 }, // Port-Au-Prince, Haiti
+ { 42, -121 }, // Portland, Oregon
+ { 61, -20 }, // Reykjavik, Iceland
+ { -22, -46 }, // Rio de Janeiro
+ { 27, -101 }, // San Antonio, Texas
+ { 34, 126 }, // Seoul, Korea
+ { 37, -87 }, // Saint Louis, Missouri
+ { 60, 30 }, // Saint Petersberg, Russia
+ { 56, 12 }, // Stockholm, Sweden
+ { 51, 105 }, // Svortalsk, Siberia
+ { 36, -96 } // Tulsa, Oklahoma
+};
+
+const int16 GlobeGame::_targetSilo[kNumTargetSilos] = {
+ 14, 9, 1, 33, 6, 8, 34, 31, 38, 21
+};
+
+const short GlobeGame::_timeLimit[kNumTargetSilos] = {
+ 120, 110, 100, 90, 80, 70, 60, 50, 40, 30
+};
+
+const TimeValue GlobeGame::_siloName[kNumTargetSilos][2] = {
+ { kHonoluluIn, kHonoluluOut },
+ { kDublinIn, kDublinOut },
+ { kAddisAbabaIn, kAddisAbabaOut },
+ { kSanAntonioIn, kSanAntonioOut },
+ { kBangkokIn, kBangkokOut },
+ { kBonnIn, kBonnOut },
+ { kSeoulIn, kSeoulOut },
+ { kReykjavikIn, kReykjavikOut },
+ { kSvortalskIn, kSvortalskOut },
+ { kMadridIn, kMadridOut }
+};
+
+// From globe room models
+
+static const GlobeGame::Point3D kCameraLocation = { 0.53f, 4.4f, -0.86f };
+static const GlobeGame::Point3D kGlobeCenter = { -31.5f, 8.0f, 0.0f };
+static const float kGlobeRadius = 8.25f;
+static const int16 kDegreesPerLongSlice = 360 / kNumLongSlices;
+static const int16 kDegreesPerLatSlice = 25;
+static const int16 kLongOrigin = -95;
+
+// Other constants.
+
+static const float kTanFieldOfView = 0.7082373180482f;
+static const float kPicturePlaneDistance = 10.0f; // Completely arbitrary.
+static const int16 kLatError = 2;
+static const int16 kLongError = 2;
+static const TimeValue kGlobeMovieStartTime = 2 * 2 * kNumLongSlices * 600 / 15;
+
+static const TimeValue kTimePerGlobeFrame = 40;
+
+static const NotificationFlags kGlobeSplash1Finished = 1;
+static const NotificationFlags kGlobeTimerExpired = kGlobeSplash1Finished << 1;
+static const NotificationFlags kMaxDeactivatedFinished = kGlobeTimerExpired << 1;
+
+static const NotificationFlags kGlobeNotificationFlags = kGlobeSplash1Finished |
+ kGlobeTimerExpired |
+ kMaxDeactivatedFinished;
+
+static const int16 kSplash1End = 4;
+static const int16 kSplash2End = 5;
+static const int16 kSplash3Start = 8;
+static const int16 kSplash3Stop = 9;
+static const int16 kSplash4Start = 9;
+static const int16 kSplash4Stop = 10;
+static const int16 kNewLaunchSiloTime = 10;
+static const int16 kSiloDeactivatedTime = 11;
+static const int16 kMissileLaunchedTime = 12;
+static const int16 kMaxDeactivatedStart = 13;
+static const int16 kMaxDeactivatedStop = 23;
+
+static const int16 kGamePlaying = 1;
+static const int16 kGameOver = 2;
+
+enum {
+ kGameIntro,
+ kPlayingRobotIntro,
+ kPlayingStrikeAuthorized,
+ kPlayingPrimaryTarget,
+ kPlayingNewSilo1,
+ kPlayingNewSilo2,
+ kPlayingNewSilo3,
+ kPlayingTime,
+ kPlayingInstructions,
+ kWaitingForPlayer,
+ kSiloDeactivated,
+ kRobotTaunting,
+ kDelayingPlayer,
+ kPlayerWon1,
+ kPlayerWon2,
+ kPlayerLost1
+};
+
+// TODO: Use ScummVM equivalent
+static const float kPI = 3.1415926535f;
+
+float degreesToRadians(float angle) {
+ return (angle * kPI) / 180;
+}
+
+float radiansToDegrees(float angle) {
+ return (angle * 180) / kPI;
+}
+
+GlobeGame::GlobeGame(Neighborhood *handler) : GameInteraction(kNoradGlobeGameInteractionID, handler),
+ _monitorMovie(kGlobeMonitorID), _globeMovie(kGlobeMovieID), _upperNamesMovie(kGlobeUpperNamesID),
+ _lowerNamesMovie(kGlobeLowerNamesID), _globeNotification(kNoradGlobeNotificationID, (PegasusEngine *)g_engine),
+ _globeCircleLeft(kGlobeCircleLeftID), _globeCircleRight(kGlobeCircleRightID),
+ _globeCircleUp(kGlobeCircleUpID), _globeCircleDown(kGlobeCircleDownID),
+ _motionHighlightLeft(kMotionHiliteLeftID), _motionHighlightRight(kMotionHiliteRightID),
+ _motionHighlightUp(kMotionHiliteUpID), _motionHighlightDown(kMotionHiliteDownID),
+ _targetHighlightUpperLeft(kTargetHiliteUpperLeftID), _targetHighlightUpperRight(kTargetHiliteUpperRightID),
+ _targetHighlightLowerLeft(kTargetHiliteLowerLeftID), _targetHighlightLowerRight(kTargetHiliteLowerRightID),
+ _globeTracker(&_globeMovie, &_motionHighlightLeft, &_motionHighlightRight, &_motionHighlightUp,
+ &_motionHighlightDown), _countdown(kGlobeCountdownID) {
+ _neighborhoodNotification = handler->getNeighborhoodNotification();
+}
+
+void GlobeGame::openInteraction() {
+ _monitorMovie.initFromMovieFile("Images/Norad Delta/N79 Left Monitor");
+ _monitorMovie.moveElementTo(kGlobeMonitorLeft, kGlobeMonitorTop);
+ _monitorMovie.setDisplayOrder(kGlobeMonitorLayer);
+ _monitorMovie.startDisplaying();
+ _monitorMovie.setSegment(0, kSplash1End * _monitorMovie.getScale());
+ _monitorMovie.show();
+
+ _monitorCallBack.setNotification(&_globeNotification);
+ _monitorCallBack.initCallBack(&_monitorMovie, kCallBackAtExtremes);
+ _monitorCallBack.setCallBackFlag(kGlobeSplash1Finished);
+ _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ _upperNamesMovie.initFromMovieFile("Images/Norad Delta/Upper Names");
+ _upperNamesMovie.moveElementTo(kGlobeUpperNamesLeft, kGlobeUpperNamesTop);
+ _upperNamesMovie.setDisplayOrder(kGlobeUpperNamesLayer);
+ _upperNamesMovie.startDisplaying();
+
+ _lowerNamesMovie.initFromMovieFile("Images/Norad Delta/Lower Names");
+ _lowerNamesMovie.moveElementTo(kGlobeLowerNamesLeft, kGlobeLowerNamesTop);
+ _lowerNamesMovie.setDisplayOrder(kGlobeLowerNamesLayer);
+ _lowerNamesMovie.startDisplaying();
+
+ _globeMovie.initFromMovieFile("Images/Norad Delta/Spinning Globe");
+ _globeMovie.moveElementTo(kGlobeLeft, kGlobeTop);
+ _globeMovie.setDisplayOrder(kGlobeMovieLayer);
+ _globeMovie.startDisplaying();
+ _globeMovie.setTime(kGlobeMovieStartTime);
+ _globeMovie.redrawMovieWorld();
+
+ _globeCircleLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleLeftPICTID, true);
+ _globeCircleLeft.moveElementTo(kGlobeCircleLeftLeft, kGlobeCircleLeftTop);
+ _globeCircleLeft.setDisplayOrder(kGlobeCircleLayer);
+ _globeCircleLeft.startDisplaying();
+
+ _globeCircleRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleRightPICTID, true);
+ _globeCircleRight.moveElementTo(kGlobeCircleRightLeft, kGlobeCircleRightTop);
+ _globeCircleRight.setDisplayOrder(kGlobeCircleLayer);
+ _globeCircleRight.startDisplaying();
+
+ _globeCircleUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleUpPICTID, true);
+ _globeCircleUp.moveElementTo(kGlobeCircleUpLeft, kGlobeCircleUpTop);
+ _globeCircleUp.setDisplayOrder(kGlobeCircleLayer);
+ _globeCircleUp.startDisplaying();
+
+ _globeCircleDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleDownPICTID, true);
+ _globeCircleDown.moveElementTo(kGlobeCircleDownLeft, kGlobeCircleDownTop);
+ _globeCircleDown.setDisplayOrder(kGlobeCircleLayer);
+ _globeCircleDown.startDisplaying();
+
+ _motionHighlightLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteLeftPICTID, true);
+ _motionHighlightLeft.moveElementTo(kGlobeLeftMotionHiliteLeft, kGlobeLeftMotionHiliteTop);
+ _motionHighlightLeft.setDisplayOrder(kGlobeHilitesLayer);
+ _motionHighlightLeft.startDisplaying();
+
+ _motionHighlightRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteRightPICTID, true);
+ _motionHighlightRight.moveElementTo(kGlobeRightMotionHiliteLeft, kGlobeRightMotionHiliteTop);
+ _motionHighlightRight.setDisplayOrder(kGlobeCircleLayer);
+ _motionHighlightRight.startDisplaying();
+
+ _motionHighlightUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteUpPICTID, true);
+ _motionHighlightUp.moveElementTo(kGlobeUpMotionHiliteLeft, kGlobeUpMotionHiliteTop);
+ _motionHighlightUp.setDisplayOrder(kGlobeHilitesLayer);
+ _motionHighlightUp.startDisplaying();
+
+ _motionHighlightDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteDownPICTID, true);
+ _motionHighlightDown.moveElementTo(kGlobeDownMotionHiliteLeft, kGlobeDownMotionHiliteTop);
+ _motionHighlightDown.setDisplayOrder(kGlobeHilitesLayer);
+ _motionHighlightDown.startDisplaying();
+
+ _targetHighlightUpperLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperLeftPICTID, true);
+ _targetHighlightUpperLeft.moveElementTo(kGlobeUpperLeftHiliteLeft, kGlobeUpperLeftHiliteTop);
+ _targetHighlightUpperLeft.setDisplayOrder(kGlobeHilitesLayer);
+ _targetHighlightUpperLeft.startDisplaying();
+
+ _targetHighlightUpperRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperRightPICTID, true);
+ _targetHighlightUpperRight.moveElementTo(kGlobeUpperRightHiliteLeft, kGlobeUpperRightHiliteTop);
+ _targetHighlightUpperRight.setDisplayOrder(kGlobeHilitesLayer);
+ _targetHighlightUpperRight.startDisplaying();
+
+ _targetHighlightLowerLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerLeftPICTID, true);
+ _targetHighlightLowerLeft.moveElementTo(kGlobeLowerLeftHiliteLeft, kGlobeLowerLeftHiliteTop);
+ _targetHighlightLowerLeft.setDisplayOrder(kGlobeHilitesLayer);
+ _targetHighlightLowerLeft.startDisplaying();
+
+ _targetHighlightLowerRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerRightPICTID, true);
+ _targetHighlightLowerRight.moveElementTo(kGlobeLowerRightHiliteLeft, kGlobeLowerRightHiliteTop);
+ _targetHighlightLowerRight.setDisplayOrder(kGlobeHilitesLayer);
+ _targetHighlightLowerRight.startDisplaying();
+
+ _countdown.setDisplayOrder(kGlobeCountdownLayer);
+ _countdown.moveElementTo(kGlobeCountdownLeft, kGlobeCountdownTop);
+ _countdown.startDisplaying();
+ _countdown.setCountdownTime(_timeLimit[0]);
+
+ _countdownCallBack.setNotification(&_globeNotification);
+ _countdownCallBack.initCallBack(&_countdown, kCallBackAtExtremes);
+ _countdownCallBack.setCallBackFlag(kGlobeTimerExpired);
+ _countdownCallBack.scheduleCallBack(kTriggerAtStart, 0, 0);
+
+ _globeNotification.notifyMe(this, kGlobeNotificationFlags, kGlobeNotificationFlags);
+
+ _gameState = kGameIntro;
+ _currentSiloIndex = 0;
+ _playedInstructions = false;
+
+ _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, kDelayCompletedFlag | kSpotSoundCompletedFlag);
+}
+
+void GlobeGame::initInteraction() {
+ _monitorMovie.start();
+ _monitorMovie.redrawMovieWorld();
+}
+
+void GlobeGame::closeInteraction() {
+ _monitorMovie.stop();
+ _monitorMovie.stopDisplaying();
+ _monitorMovie.releaseMovie();
+ _monitorCallBack.releaseCallBack();
+
+ _globeMovie.stop();
+ _globeMovie.stopDisplaying();
+ _globeMovie.releaseMovie();
+ _globeNotification.cancelNotification(this);
+
+ _upperNamesMovie.stop();
+ _upperNamesMovie.stopDisplaying();
+ _upperNamesMovie.releaseMovie();
+
+ _lowerNamesMovie.stop();
+ _lowerNamesMovie.stopDisplaying();
+ _lowerNamesMovie.releaseMovie();
+
+ _countdown.hide();
+ _countdown.stopDisplaying();
+ _countdownCallBack.releaseCallBack();
+
+ _globeCircleLeft.stopDisplaying();
+ _globeCircleLeft.deallocateSurface();
+ _globeCircleRight.stopDisplaying();
+ _globeCircleRight.deallocateSurface();
+ _globeCircleUp.stopDisplaying();
+ _globeCircleUp.deallocateSurface();
+ _globeCircleDown.stopDisplaying();
+ _globeCircleDown.deallocateSurface();
+
+ _motionHighlightLeft.stopDisplaying();
+ _motionHighlightLeft.deallocateSurface();
+ _motionHighlightRight.stopDisplaying();
+ _motionHighlightRight.deallocateSurface();
+ _motionHighlightUp.stopDisplaying();
+ _motionHighlightUp.deallocateSurface();
+ _motionHighlightDown.stopDisplaying();
+ _motionHighlightDown.deallocateSurface();
+
+ _targetHighlightUpperLeft.stopDisplaying();
+ _targetHighlightUpperLeft.deallocateSurface();
+ _targetHighlightUpperRight.stopDisplaying();
+ _targetHighlightUpperRight.deallocateSurface();
+ _targetHighlightLowerLeft.stopDisplaying();
+ _targetHighlightLowerLeft.deallocateSurface();
+ _targetHighlightLowerRight.stopDisplaying();
+ _targetHighlightLowerRight.deallocateSurface();
+
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void GlobeGame::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ TimeScale scale = _monitorMovie.getScale();
+
+ if (notification == _neighborhoodNotification) {
+ switch (_gameState) {
+ case kPlayingRobotIntro:
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(0, _monitorMovie.getDuration());
+ _monitorMovie.setTime(kSplash2End * scale - 1);
+ _monitorMovie.setFlags(0);
+
+ _owner->requestDelay(1, 2, kFilterNoInput, 0);
+ _owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ _gameState = kPlayingStrikeAuthorized;
+ break;
+ case kPlayingStrikeAuthorized:
+ _monitorMovie.setSegment(kSplash3Start * scale, kSplash3Stop * scale);
+ _monitorMovie.setTime(kSplash3Start * scale);
+ _monitorMovie.redrawMovieWorld();
+
+ _owner->requestDelay(1, 3, kFilterNoInput, 0);
+ _owner->requestSpotSound(kPrimaryTargetIn, kPrimaryTargetOut, kFilterNoInput, 0);
+ _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ _monitorMovie.start();
+ _gameState = kPlayingPrimaryTarget;
+ break;
+ case kPlayingPrimaryTarget:
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(0, _monitorMovie.getDuration());
+ _monitorMovie.setTime(kNewLaunchSiloTime * scale);
+ _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ _gameState = kPlayingNewSilo1;
+ break;
+ case kPlayingNewSilo1:
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(0, _monitorMovie.getDuration());
+ _owner->requestDelay(1, 3, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingNewSilo2;
+ break;
+ case kPlayingNewSilo2:
+ _upperNamesMovie.show();
+ _upperNamesMovie.setTime(_currentSiloIndex * _upperNamesMovie.getScale());
+ _upperNamesMovie.redrawMovieWorld();
+ _monitorMovie.setTime(kSplash4Stop * scale - 1);
+ _monitorMovie.redrawMovieWorld();
+ _owner->requestSpotSound(_siloName[_currentSiloIndex][0], _siloName[_currentSiloIndex][1], kFilterNoInput, 0);
+ _owner->requestDelay(1, 3, kFilterNoInput, 0);
+ _owner->requestSpotSound(kLaunchToProceedIn, kLaunchToProceedOut, kFilterNoInput, 0);
+ _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingNewSilo3;
+ break;
+ case kPlayingNewSilo3:
+ _countdown.stopCountdown();
+ _countdown.setCountdownTime(_timeLimit[_currentSiloIndex]);
+ _countdown.show();
+ _gameState = kPlayingTime;
+
+ if (_timeLimit[_currentSiloIndex] >= 120)
+ _owner->requestSpotSound(kTwoMinutesIn, kTwoMinutesOut, kFilterNoInput, 0);
+ else if (_timeLimit[_currentSiloIndex] >= 60)
+ _owner->requestSpotSound(kOneMinuteIn, kOneMinuteOut, kFilterNoInput, 0);
+
+ switch (_timeLimit[_currentSiloIndex] % 60) {
+ case 0:
+ _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ break;
+ case 10:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kTenSecondsIn, kTenSecondsOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ break;
+ case 20:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kTwentySecondsIn, kTwentySecondsOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 30:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kThirtySecondsIn, kThirtySecondsOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 40:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kFortySecondsIn, kFortySecondsOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 50:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kFiftySecondsIn, kFiftySecondsOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ }
+ case kPlayingTime:
+ _gameState = kPlayingInstructions;
+ _globeMovie.show();
+ _globeCircleLeft.show();
+ _globeCircleRight.show();
+ _globeCircleUp.show();
+ _globeCircleDown.show();
+
+ if (_playedInstructions) {
+ receiveNotification(notification, flags);
+ } else {
+ _owner->requestSpotSound(kToDeactivateIn, kToDeactivateOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ _playedInstructions = true;
+ }
+ break;
+ case kPlayingInstructions:
+ _gameState = kWaitingForPlayer;
+ _countdown.startCountdown();
+ break;
+ case kSiloDeactivated:
+ _gameState = kRobotTaunting;
+
+ switch (_currentSiloIndex) {
+ case 3:
+ _owner->requestSpotSound(kYouCannotPossiblyIn, kYouCannotPossiblyOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 5:
+ _owner->requestSpotSound(kYouWillFailIn, kYouWillFailOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ break;
+ case 7:
+ _owner->requestSpotSound(kGiveUpHumanIn, kGiveUpHumanOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ break;
+ case 9:
+ _owner->requestSpotSound(kYouAreRunningIn, kYouAreRunningOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ default:
+ _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ _monitorMovie.setTime(kNewLaunchSiloTime * scale);
+ _monitorMovie.redrawMovieWorld();
+ _gameState = kPlayingNewSilo1;
+ break;
+ }
+ break;
+ case kRobotTaunting:
+ _owner->requestDelay(1, 1, kFilterNoInput, 0);
+ _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ _monitorMovie.setTime(kNewLaunchSiloTime * scale);
+ _monitorMovie.redrawMovieWorld();
+ _gameState = kPlayingNewSilo1;
+ break;
+ case kDelayingPlayer:
+ _gameState = kWaitingForPlayer;
+ break;
+ case kPlayerLost1:
+ _owner->recallToTSAFailure();
+ break;
+ case kPlayerWon2:
+ ((NoradDelta *)_owner)->finishedGlobeGame();
+ _owner->requestDeleteCurrentInteraction();
+ break;
+ default:
+ break;
+ }
+ } else if (notification == &_globeNotification) {
+ ExtraTable::Entry entry;
+
+ switch (flags) {
+ case kGlobeSplash1Finished:
+ _owner->getExtraEntry(kN79BrightView, entry);
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(kSplash1End * scale, kSplash2End * scale);
+ _monitorMovie.setFlags(kLoopTimeBase);
+ _monitorMovie.start();
+ _owner->showViewFrame(entry.movieStart);
+ _owner->requestSpotSound(kIJustBrokeIn, kIJustBrokeOut, kFilterNoInput, 0);
+ _owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingRobotIntro;
+ break;
+ case kGlobeTimerExpired:
+ // Missile launched, player loses.
+ _owner->requestSpotSound(kMissileLaunchedIn, kMissileLaunchedOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ _gameState = kPlayerLost1;
+ break;
+ case kMaxDeactivatedFinished:
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(0, _monitorMovie.getDuration());
+ _owner->requestDelay(1, 2, kFilterNoInput, 0);
+ _owner->requestSpotSound(kTheOnlyGoodHumanIn, kTheOnlyGoodHumanOut, kFilterNoInput, 0);
+ _owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayerWon2;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// Prevent the player from getting up until the game is over.
+
+void GlobeGame::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ Common::Point where;
+ input.getInputLocation(where);
+ Hotspot *spot = g_allHotspots.findHotspot(where);
+
+ if (((PegasusEngine *)g_engine)->_cursor->isVisible() && spot != 0 &&
+ spot->getObjectID() == kNorad79SiloAreaSpotID && findClickedSilo(input) != -1) {
+ _targetHighlightUpperLeft.show();
+ _targetHighlightUpperRight.show();
+ _targetHighlightLowerLeft.show();
+ _targetHighlightLowerRight.show();
+ } else {
+ _targetHighlightUpperLeft.hide();
+ _targetHighlightUpperRight.hide();
+ _targetHighlightLowerLeft.hide();
+ _targetHighlightLowerRight.hide();
+ }
+
+ // Interrupt certain inputs to prevent player from switching modes.
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+int16 GlobeGame::findClickedSilo(const Input &input) {
+ Common::Point screenPoint;
+ input.getInputLocation(screenPoint);
+ screenPoint.x -= kNavAreaLeft;
+ screenPoint.y -= kNavAreaTop;
+
+ Line3D ray;
+ screenPointTo3DPoint(screenPoint.x, screenPoint.y, ray.pt2);
+ ray.pt1 = kCameraLocation;
+
+ Point3D globePoint;
+ if (lineHitsGlobe(ray, globePoint)) {
+ int16 latOrigin, longOrigin, latitude, longitude;
+ globeMovieFrameToOrigin(_globeMovie.getTime() / kTimePerGlobeFrame, latOrigin, longOrigin);
+ globePointToLatLong(globePoint, latOrigin, longOrigin, latitude, longitude);
+
+ for (int16 i = 0; i < kNumAllSilos; i++)
+ if (_siloCoords[i][0] >= latitude - kLatError && _siloCoords[i][0] <= latitude + kLatError &&
+ _siloCoords[i][1] >= longitude - kLongError && _siloCoords[i][1] <= longitude + kLongError)
+ return i;
+ }
+
+ return -1;
+}
+
+void GlobeGame::spinGlobe(const Input &input, const Hotspot *spot, GlobeTrackDirection trackDirection) {
+ _globeTracker.setTrackParameters(spot, trackDirection);
+ _globeTracker.startTracking(input);
+}
+
+void GlobeGame::clickGlobe(const Input &input) {
+ int16 newSilo = findClickedSilo(input);
+
+ if (newSilo != -1) {
+ _targetHighlightUpperLeft.hide();
+ _targetHighlightUpperRight.hide();
+ _targetHighlightLowerLeft.hide();
+ _targetHighlightLowerRight.hide();
+ _lowerNamesMovie.show();
+ _lowerNamesMovie.setTime(newSilo * _lowerNamesMovie.getScale());
+ _lowerNamesMovie.redrawMovieWorld();
+ _owner->requestSpotSound(kSiloBeepIn, kSiloBeepOut, kFilterNoInput, 0);
+
+ if (newSilo == _targetSilo[_currentSiloIndex]) {
+ _currentSiloIndex++;
+ _countdown.stopCountdown();
+ _owner->requestSpotSound(kSiloDeactivatedIn, kSiloDeactivatedOut, kFilterNoInput, 0);
+
+ if (_currentSiloIndex == kNumTargetSilos) {
+ // Player won.
+ _owner->requestDelay(1, 2, kFilterNoInput, 0);
+ _upperNamesMovie.hide();
+ _lowerNamesMovie.hide();
+ _countdown.hide();
+ _monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(),
+ kMaxDeactivatedStop * _monitorMovie.getScale());
+ _monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale());
+ _monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished);
+ _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _monitorMovie.start();
+ _owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ _gameState = kPlayerWon1;
+ } else {
+ _owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
+ _upperNamesMovie.hide();
+ _lowerNamesMovie.hide();
+ _countdown.hide();
+ _monitorMovie.setTime(kSiloDeactivatedTime * _monitorMovie.getScale());
+ _monitorMovie.redrawMovieWorld();
+ _gameState = kSiloDeactivated;
+ }
+ } else {
+ _owner->requestDelay(5, 1, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kDelayingPlayer;
+ // Play "incorrect" sound?
+ }
+ }
+}
+
+void GlobeGame::clickInHotspot(const Input &input, const Hotspot *spot) {
+ switch (spot->getObjectID()) {
+ case kNorad79SpinLeftSpotID:
+ spinGlobe(input, spot, kTrackLeft);
+ break;
+ case kNorad79SpinRightSpotID:
+ spinGlobe(input, spot, kTrackRight);
+ break;
+ case kNorad79SpinUpSpotID:
+ spinGlobe(input, spot, kTrackUp);
+ break;
+ case kNorad79SpinDownSpotID:
+ spinGlobe(input, spot, kTrackDown);
+ break;
+ case kNorad79SiloAreaSpotID:
+ clickGlobe(input);
+ break;
+ default:
+ GameInteraction::clickInHotspot(input, spot);
+ break;
+ }
+}
+
+void GlobeGame::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ switch (_gameState) {
+ case kWaitingForPlayer:
+ g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SpinLeftSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SpinRightSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SpinUpSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SpinDownSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SiloAreaSpotID);
+ break;
+ default:
+ g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID);
+ break;
+ }
+}
+
+void GlobeGame::globeMovieFrameToOrigin(int16 frameNum, int16 &latOrigin, int16 &longOrigin) {
+ latOrigin = kDegreesPerLatSlice * 2 - (frameNum / (kNumLongSlices * 2)) * kDegreesPerLatSlice;
+ frameNum %= kNumLongSlices * 2;
+
+ if (frameNum >= kNumLongSlices)
+ longOrigin = kLongOrigin + (kNumLongSlices * 2 - 1 - frameNum) * kDegreesPerLongSlice;
+ else
+ longOrigin = kLongOrigin + frameNum * kDegreesPerLongSlice;
+
+ if (longOrigin > 180)
+ longOrigin -= 360;
+}
+
+void GlobeGame::globePointToLatLong(const GlobeGame::Point3D &pt, int16 latOrigin, int16 longOrigin,
+ int16 &latitude, int16 &longitude) {
+ Point3D scratch = pt;
+
+ // Translate globe center to origin.
+ scratch.x -= kGlobeCenter.x;
+ scratch.y -= kGlobeCenter.y;
+ scratch.z -= kGlobeCenter.z;
+
+ // Rotate around z axis latOrigin degrees to bring equator parallel with XZ plane
+ float theta = degreesToRadians(latOrigin);
+ float s = sin(theta);
+ float c = cos(theta);
+ float x = scratch.x * c - scratch.y * s;
+ float y = scratch.y * c + scratch.x * s;
+ scratch.x = x;
+ scratch.y = y;
+
+ // Calculate latitude
+ latitude = (int16)radiansToDegrees(asin(scratch.y / kGlobeRadius));
+
+ // Rotate around y axis longOrigin degrees to bring longitude 0 to positive X axis
+ theta = degreesToRadians(longOrigin);
+ s = sin(theta);
+ c = cos(theta);
+ x = scratch.x * c - scratch.z * s;
+ float z = scratch.z * c + scratch.x * s;
+ scratch.x = x;
+ scratch.z = z;
+
+ // Calculate longitude
+ longitude = (int16)radiansToDegrees(acos(scratch.x / sqrt(scratch.x * scratch.x + scratch.z * scratch.z)));
+
+ if (scratch.z < 0)
+ longitude = -longitude;
+}
+
+// h, v in [0, 511][0, 255]
+// Looking down negative x axis.
+void GlobeGame::screenPointTo3DPoint(int16 h, int16 v, GlobeGame::Point3D &pt) {
+ pt.x = kCameraLocation.x - kPicturePlaneDistance;
+ pt.y = kCameraLocation.y + (128 - v) * kPicturePlaneDistance * kTanFieldOfView / 256;
+ pt.z = kCameraLocation.z + (h - 256) * kPicturePlaneDistance * kTanFieldOfView / 256;
+}
+
+// Fundamentals of Three-Dimensional Graphics, by Alan Watt
+// pp. 163-164
+bool GlobeGame::lineHitsGlobe(const GlobeGame::Line3D &line, GlobeGame::Point3D &pt) {
+ float i = line.pt2.x - line.pt1.x;
+ float j = line.pt2.y - line.pt1.y;
+ float k = line.pt2.z - line.pt1.z;
+ float a = i * i + j * j + k * k;
+ float b = 2 * i * (line.pt1.x - kGlobeCenter.x) + 2 * j * (line.pt1.y - kGlobeCenter.y) +
+ 2 * k * (line.pt1.z - kGlobeCenter.z);
+ float c = kGlobeCenter.x * kGlobeCenter.x + kGlobeCenter.y * kGlobeCenter.y +
+ kGlobeCenter.z * kGlobeCenter.z + line.pt1.x * line.pt1.x + line.pt1.y * line.pt1.y +
+ line.pt1.z * line.pt1.z + -2 * (kGlobeCenter.x * line.pt1.x + kGlobeCenter.y * line.pt1.y +
+ kGlobeCenter.z * line.pt1.z) - kGlobeRadius * kGlobeRadius;
+
+ // Solve quadratic equation of a, b, c.
+ float t = b * b - 4 * a * c;
+
+ if (t >= 0.0f) {
+ // Return smaller root, which corresponds to closest intersection point.
+ t = (-b - sqrt(t)) / (2 * a);
+ pt.x = i * t + line.pt1.x;
+ pt.y = j * t + line.pt1.y;
+ pt.z = k * t + line.pt1.z;
+ return true;
+ }
+
+ return false;
+}
+
+bool GlobeGame::canSolve() {
+ return _gameState != kPlayerWon1 && _gameState != kPlayerWon2 && _gameState != kPlayerLost1;
+}
+
+void GlobeGame::doSolve() {
+ _owner->requestDelay(1, 2, kFilterNoInput, 0);
+ _upperNamesMovie.hide();
+ _lowerNamesMovie.hide();
+ _countdown.hide();
+ _monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(), kMaxDeactivatedStop * _monitorMovie.getScale());
+ _monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale());
+ _monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished);
+ _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _monitorMovie.start();
+ _owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ _gameState = kPlayerWon1;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.h b/engines/pegasus/neighborhood/norad/delta/globegame.h
new file mode 100644
index 0000000000..73ed48866f
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/delta/globegame.h
@@ -0,0 +1,169 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+
+namespace Pegasus {
+
+enum GlobeTrackDirection {
+ kTrackLeft,
+ kTrackRight,
+ kTrackUp,
+ kTrackDown
+};
+
+// This class assumes that the globe movie is built at 15 frames per second with a
+// time scale of 600, yielding 40 time unit per frame.
+
+class GlobeTracker : public Tracker {
+public:
+ GlobeTracker(Movie *, Picture *, Picture *, Picture *, Picture *);
+ virtual ~GlobeTracker() {}
+
+ void setTrackParameters(const Hotspot *, GlobeTrackDirection);
+ void continueTracking(const Input &);
+ void startTracking(const Input &);
+ void stopTracking(const Input &);
+ void activateHotspots();
+ bool stopTrackingInput(const Input &);
+
+protected:
+ void trackGlobeMovie();
+ void stopGlobeMovie();
+
+ Movie *_globeMovie;
+ Picture *_leftHighlight;
+ Picture *_rightHighlight;
+ Picture *_upHighlight;
+ Picture *_downHighlight;
+ const Hotspot *_trackSpot;
+ int _trackTime;
+ GlobeTrackDirection _trackDirection;
+};
+
+class GlobeCountdown : public IdlerAnimation {
+public:
+ GlobeCountdown(const DisplayElementID);
+ virtual ~GlobeCountdown() {}
+
+ void setCountdownTime(const int);
+ void startCountdown();
+ void stopCountdown();
+
+ void setDisplayOrder(const DisplayOrder);
+ void show();
+ void hide();
+ void moveElementTo(const CoordType, const CoordType);
+
+ void draw(const Common::Rect &);
+
+protected:
+ Surface _digits;
+ int16 _digitOffset;
+};
+
+static const int16 kNumAllSilos = 40;
+static const int16 kNumTargetSilos = 10;
+static const int16 kNumLongSlices = 72;
+
+class GlobeGame : public GameInteraction, public NotificationReceiver {
+public:
+ GlobeGame(Neighborhood *);
+ virtual ~GlobeGame() {}
+
+ void handleInput(const Input &, const Hotspot *);
+ void clickInHotspot(const Input &, const Hotspot *);
+ void activateHotspots();
+
+ bool canSolve();
+ void doSolve();
+
+ struct Point3D {
+ float x, y, z;
+ };
+
+ struct Line3D {
+ Point3D pt1, pt2;
+ };
+
+protected:
+ // Latitude (-90 - 90) and longitude (-180 - 180)
+ static const int16 _siloCoords[kNumAllSilos][2];
+
+ static const int16 _targetSilo[kNumTargetSilos];
+ static const int16 _timeLimit[kNumTargetSilos];
+ static const TimeValue _siloName[kNumTargetSilos][2];
+
+ void openInteraction();
+ void initInteraction();
+ void closeInteraction();
+
+ void receiveNotification(Notification *, const NotificationFlags);
+
+ void spinGlobe(const Input &, const Hotspot *, GlobeTrackDirection);
+ void clickGlobe(const Input &);
+
+ int16 findClickedSilo(const Input &);
+
+ void globeMovieFrameToOrigin(int16, int16 &, int16 &);
+ void globePointToLatLong(const Point3D &, int16, int16, int16 &, int16 &);
+ void screenPointTo3DPoint(int16, int16, Point3D &);
+ bool lineHitsGlobe(const Line3D &, Point3D &);
+
+ Movie _monitorMovie;
+ Movie _globeMovie;
+ Movie _upperNamesMovie;
+ Movie _lowerNamesMovie;
+ Notification _globeNotification;
+ NotificationCallBack _monitorCallBack;
+ GlobeTracker _globeTracker;
+ Picture _globeCircleLeft;
+ Picture _globeCircleRight;
+ Picture _globeCircleUp;
+ Picture _globeCircleDown;
+ Picture _motionHighlightLeft;
+ Picture _motionHighlightRight;
+ Picture _motionHighlightUp;
+ Picture _motionHighlightDown;
+ Picture _targetHighlightUpperLeft;
+ Picture _targetHighlightUpperRight;
+ Picture _targetHighlightLowerLeft;
+ Picture _targetHighlightLowerRight;
+ GlobeCountdown _countdown;
+ NotificationCallBack _countdownCallBack;
+ int16 _gameState;
+ int16 _currentSiloIndex;
+ Notification *_neighborhoodNotification;
+ bool _playedInstructions;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp
new file mode 100644
index 0000000000..f2ea53ff89
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp
@@ -0,0 +1,869 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/interface.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/opticalchip.h"
+#include "pegasus/items/biochips/retscanchip.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/subcontrolroom.h"
+#include "pegasus/neighborhood/norad/delta/globegame.h"
+#include "pegasus/neighborhood/norad/delta/noraddelta.h"
+
+namespace Pegasus {
+
+const uint32 NoradDelta::_noradDeltaClawExtras[22] = {
+ kN60ClawFromAToB,
+ kN60ClawALoop,
+ kN60ClawAPinch,
+ kN60ClawACounterclockwise,
+ kN60ClawAClockwise,
+ kN60ClawFromBToA,
+ kN60ClawFromBToC,
+ kN60ClawFromBToD,
+ kN60ClawBLoop,
+ kN60ClawBPinch,
+ kN60ClawBCounterclockwise,
+ kN60ClawBClockwise,
+ kN60ClawFromCToB,
+ kN60ClawCLoop,
+ kN60ClawCPinch,
+ kN60ClawCCounterclockwise,
+ kN60ClawCClockwise,
+ kN60ClawFromDToB,
+ kN60ClawDLoop,
+ kN60ClawDPinch,
+ kN60ClawDCounterclockwise,
+ kN60ClawDClockwise
+};
+
+NoradDelta::NoradDelta(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Delta", kNoradDeltaID) {
+ _elevatorUpRoomID = kNorad49South;
+ _elevatorDownRoomID = kNorad48South;
+ _elevatorUpSpotID = kNorad48ElevatorUpSpotID;
+ _elevatorDownSpotID = kNorad49ElevatorDownSpotID;
+
+ // Pressure door stuff.
+
+ _subRoomEntryRoom1 = kNorad50;
+ _subRoomEntryDir1 = kEast;
+ _subRoomEntryRoom2 = kNorad59;
+ _subRoomEntryDir2 = kWest;
+ _upperPressureDoorRoom = kNorad50East;
+ _lowerPressureDoorRoom = kNorad59West;
+
+ _upperPressureDoorUpSpotID = kDeltaUpperPressureDoorUpSpotID;
+ _upperPressureDoorDownSpotID = kDeltaUpperPressureDoorDownSpotID;
+ _upperPressureDoorAbortSpotID = kNorad50DoorOutSpotID;
+
+ _lowerPressureDoorUpSpotID = kDeltaLowerPressureDoorUpSpotID;
+ _lowerPressureDoorDownSpotID = kDeltaLowerPressureDoorDownSpotID;
+ _lowerPressureDoorAbortSpotID = kNorad59WestOutSpotID;
+
+ _pressureSoundIn = kPressureDoorIntro1In;
+ _pressureSoundOut = kPressureDoorIntro1Out;
+ _equalizeSoundIn = kPressureDoorIntro2In;
+ _equalizeSoundOut = kPressureDoorIntro2Out;
+ _accessDeniedIn = kDeltaAccessDeniedIn;
+ _accessDeniedOut = kDeltaAccessDeniedOut;
+
+ GameState.setNoradSubPrepState(kSubDamaged);
+
+ _subControlRoom = kNorad60West;
+}
+
+void NoradDelta::init() {
+ Norad::init();
+
+ // Little fix for the retinal scan zoom in spot...
+ Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(kNorad68WestSpotID);
+ hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag);
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kNorad79WestSpotID);
+ hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag);
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotShieldBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kDelta59RobotShieldBiochipSpotID);
+ hotspotEntry->hotspotItem = kShieldBiochip;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotOpMemBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta59RobotOpMemBiochipSpotID);
+ hotspotEntry->hotspotItem = kOpticalBiochip;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotRetinalBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta59RobotRetinalBiochipSpotID);
+ hotspotEntry->hotspotItem = kRetinalScanBiochip;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotShieldBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta60RobotShieldBiochipSpotID);
+ hotspotEntry->hotspotItem = kShieldBiochip;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotOpMemBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta60RobotOpMemBiochipSpotID);
+ hotspotEntry->hotspotItem = kOpticalBiochip;
+
+ hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotRetinalBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta60RobotRetinalBiochipSpotID);
+ hotspotEntry->hotspotItem = kRetinalScanBiochip;
+}
+
+void NoradDelta::start() {
+ if (g_energyMonitor) {
+ g_energyMonitor->stopEnergyDraining();
+ g_energyMonitor->restoreLastEnergyValue();
+ _vm->resetEnergyDeathReason();
+ g_energyMonitor->startEnergyDraining();
+ }
+
+ Norad::start();
+}
+
+void NoradDelta::setUpAIRules() {
+ Neighborhood::setUpAIRules();
+
+ if (g_AIArea) {
+ AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN07NE", false);
+ AILocationCondition *locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kNorad68, kWest));
+ AIRule *rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+ }
+}
+
+void NoradDelta::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) {
+ switch (entry.extra) {
+ case kArriveFromSubChase:
+ compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 20, entry.movieEnd, 90);
+ compassMove.insertFaderKnot(entry.movieStart + 25 * kNoradDeltaFrameDuration, 20);
+ compassMove.insertFaderKnot(entry.movieStart + 94 * kNoradDeltaFrameDuration, 45);
+ compassMove.insertFaderKnot(entry.movieStart + 101 * kNoradDeltaFrameDuration, 45);
+ compassMove.insertFaderKnot(entry.movieStart + 146 * kNoradDeltaFrameDuration, 90 + 15);
+ compassMove.insertFaderKnot(entry.movieStart + 189 * kNoradDeltaFrameDuration, 90 + 15);
+ compassMove.insertFaderKnot(entry.movieStart + 204 * kNoradDeltaFrameDuration, 90 + 30);
+ compassMove.insertFaderKnot(entry.movieStart + 214 * kNoradDeltaFrameDuration, 90 + 20);
+ compassMove.insertFaderKnot(entry.movieStart + 222 * kNoradDeltaFrameDuration, 90 + 20);
+ compassMove.insertFaderKnot(entry.movieStart + 228 * kNoradDeltaFrameDuration, 90 + 10);
+ compassMove.insertFaderKnot(entry.movieStart + 245 * kNoradDeltaFrameDuration, 90 + 85);
+ compassMove.insertFaderKnot(entry.movieStart + 262 * kNoradDeltaFrameDuration, 90 + 70);
+ compassMove.insertFaderKnot(entry.movieStart + 273 * kNoradDeltaFrameDuration, 90 + 80);
+ compassMove.insertFaderKnot(entry.movieStart + 287 * kNoradDeltaFrameDuration, 90);
+ break;
+ case kN60PlayerFollowsRobotToDoor:
+ compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270 + kSubControlCompassAngle,
+ entry.movieEnd, 270 - 15);
+ compassMove.insertFaderKnot(entry.movieStart + 280, 270 + kSubControlCompassAngle);
+ compassMove.insertFaderKnot(entry.movieStart + 920, 360);
+ compassMove.insertFaderKnot(entry.movieStart + 1840, 360);
+ compassMove.insertFaderKnot(entry.movieStart + 2520, 270);
+ compassMove.insertFaderKnot(entry.movieStart + 3760, 270);
+ compassMove.insertFaderKnot(entry.movieStart + 4640, 270 + kSubControlCompassAngle);
+ break;
+ case kN59PlayerWins2:
+ compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270, entry.movieEnd, 280);
+ compassMove.insertFaderKnot(entry.movieEnd - 1000, 270);
+ default:
+ Norad::getExtraCompassMove(entry, compassMove);
+ break;
+ }
+}
+
+GameInteraction *NoradDelta::makeInteraction(const InteractionID interactionID) {
+ if (interactionID == kNoradGlobeGameInteractionID)
+ return new GlobeGame(this);
+
+ return Norad::makeInteraction(interactionID);
+}
+
+void NoradDelta::playClawMonitorIntro() {
+ playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut);
+}
+
+void NoradDelta::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) {
+ Norad::getExitEntry(room, direction, entry);
+
+ if (room == kNorad61 && direction == kSouth)
+ entry.movieStart += kNoradDeltaFrameDuration;
+}
+
+void NoradDelta::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) {
+ Norad::getZoomEntry(id, zoomEntry);
+
+ if (id == kNorad59WestSpotID && GameState.getNoradPlayedGlobeGame()) {
+ ExtraTable::Entry extraEntry;
+ getExtraEntry(kN59ZoomWithRobot, extraEntry);
+ zoomEntry.movieStart = extraEntry.movieStart;
+ zoomEntry.movieEnd = extraEntry.movieEnd;
+ }
+}
+
+void NoradDelta::loadAmbientLoops() {
+/*
+ Logic:
+
+ loop sound 1:
+ if room == kNorad79West
+ if player globe game
+ play kNoradGlobeLoop2SoundNum
+ else
+ play kNoradRedAlertLoopSoundNum
+ else if room >= kNorad78 && room <= kNorad79
+ play kNoradGlobeLoop2SoundNum
+ else if gassed,
+ if room >= kNorad41 && room <= kNorad49South
+ play kNoradNewSubLoopSoundNum, kNoradWarningVolume
+ else if room >= kNorad59 && room <= kNorad60West
+ play kNoradSubControlLoopSoundNum, kNoradWarningVolume
+ else
+ play kNoradWarningLoopSoundNum, kNoradWarningVolume
+ else
+ play nothing
+ loop sound 2:
+ if gassed and not wearing air mask
+ if room == kNorad54North
+ play breathing unmanned loop
+ else
+ play breathing
+ else
+ if room == kNorad54North
+ play unmanned loop
+ else
+ play nothing
+*/
+
+ if (GameState.getNoradArrivedFromSub()) {
+ RoomID room = GameState.getCurrentRoom();
+
+ if (room == kNorad79West) {
+ if (_privateFlags.getFlag(kNoradPrivateFinishedGlobeGameFlag))
+ loadLoopSound1("Sounds/Norad/GlobAmb2.22K.AIFF");
+ else
+ loadLoopSound1("Sounds/Norad/RedAlert.22K.AIFF");
+ } else if (room >= kNorad78 && room <= kNorad79) {
+ // clone2727 says: This looks like it should be loadLoopSound1...
+ loadLoopSound2("Sounds/Norad/RedAlert.22K.AIFF");
+ } else if (GameState.getNoradGassed()) {
+ if (room >= kNorad41 && room <= kNorad49South)
+ loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
+ else if (room >= kNorad59 && room <= kNorad60West)
+ loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
+ else
+ loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume);
+ } else {
+ loadLoopSound1("");
+ }
+
+ if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) {
+ if (room == kNorad54North)
+ loadLoopSound2("Sounds/Norad/Breathing Typing.22K.AIFF", 0x100 / 2);
+ else
+ loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0);
+ } else {
+ if (room == kNorad54North)
+ loadLoopSound2("Sounds/Norad/N54NAS.22K.AIFF", 0x100 / 2);
+ else
+ loadLoopSound2("");
+ }
+ } else {
+ // Start them off at zero...
+ if (GameState.getNoradGassed())
+ loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", 0, 0, 0);
+ if (!g_airMask->isAirFilterOn())
+ loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", 0, 0, 0);
+ }
+}
+
+void NoradDelta::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kNorad41, kEast):
+ case MakeRoomView(kNorad49, kEast):
+ case MakeRoomView(kNorad49, kWest):
+ case MakeRoomView(kNorad61, kSouth):
+ case MakeRoomView(kNorad68, kEast):
+ case MakeRoomView(kNorad79, kWest):
+ makeContinuePoint();
+ break;
+ }
+}
+
+void NoradDelta::arriveAt(const RoomID room, const DirectionConstant direction) {
+ if (room != kNorad68)
+ GameState.setNoradRetScanGood(false);
+
+ Norad::arriveAt(room, direction);
+
+ FaderMoveSpec loop1Spec, loop2Spec;
+ ExtraTable::Entry entry;
+
+ switch (room) {
+ case kNorad41:
+ if (direction == kEast && !GameState.getNoradArrivedFromSub()) {
+ GameState.setNoradPlayedGlobeGame(false);
+
+ GameState.setNoradBeatRobotWithClaw(false);
+ GameState.setNoradBeatRobotWithDoor(false);
+ GameState.setNoradRetScanGood(false);
+
+ GameState.setScoringExitedSub(true);
+
+ getExtraEntry(kArriveFromSubChase, entry);
+
+ loop1Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd -
+ entry.movieStart, kNoradWarningVolume);
+ loop1Spec.insertFaderKnot(7320, 0);
+ loop1Spec.insertFaderKnot(7880, kNoradWarningVolume);
+
+ loop2Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd -
+ entry.movieStart, kNoradSuckWindVolume);
+ loop1Spec.insertFaderKnot(7320, 0);
+ loop1Spec.insertFaderKnot(7880, kNoradSuckWindVolume);
+
+ startExtraSequence(kArriveFromSubChase, kExtraCompletedFlag, kFilterNoInput);
+
+ startLoop1Fader(loop1Spec);
+ startLoop2Fader(loop2Spec);
+ }
+ break;
+ case kNorad54North:
+ GameState.setScoringSawRobotAt54North(true);
+ break;
+ case kNorad68:
+ if (GameState.getNoradRetScanGood())
+ openDoor();
+ break;
+ case kNorad68West:
+ arriveAtNorad68West();
+ break;
+ case kNorad79West:
+ arriveAtNorad79West();
+ break;
+ default:
+ break;
+ }
+}
+
+void NoradDelta::doorOpened() {
+ Norad::doorOpened();
+ GameState.setNoradRetScanGood(false);
+}
+
+void NoradDelta::arriveAtNorad68West() {
+ playSpotSoundSync(kHoldForRetinalIn, kHoldForRetinalOut);
+
+ BiochipItem *retScan = _vm->getCurrentBiochip();
+
+ if (retScan != 0 && retScan->getObjectID() == kRetinalScanBiochip) {
+ ((RetScanChip *)retScan)->searchForLaser();
+ succeedRetinalScan();
+ } else {
+ failRetinalScan();
+ }
+}
+
+void NoradDelta::arriveAtNorad79West() {
+ if (!GameState.getNoradPlayedGlobeGame())
+ newInteraction(kNoradGlobeGameInteractionID);
+}
+
+void NoradDelta::bumpIntoWall() {
+ requestSpotSound(kDeltaBumpIntoWallIn, kDeltaBumpIntoWallOut, kFilterNoInput, 0);
+ Neighborhood::bumpIntoWall();
+}
+
+void NoradDelta::failRetinalScan() {
+ startExtraSequence(kNoradDeltaRetinalScanBad, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void NoradDelta::succeedRetinalScan() {
+ startExtraSequence(kNoradDeltaRetinalScanGood, kExtraCompletedFlag, kFilterNoInput);
+ GameState.setNoradRetScanGood(true);
+ GameState.setScoringUsedRetinalChip(true);
+}
+
+void NoradDelta::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &entry) {
+ Norad::getDoorEntry(room, direction, entry);
+
+ if (room == kNorad68 && direction == kWest && !GameState.getNoradRetScanGood())
+ entry.flags = kDoorPresentMask | kDoorLockedMask;
+}
+
+void NoradDelta::finishedGlobeGame() {
+ GameState.setNoradPlayedGlobeGame(true);
+ _privateFlags.setFlag(kNoradPrivateFinishedGlobeGameFlag, true);
+ GameState.setScoringFinishedGlobeGame(true);
+ loadAmbientLoops();
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN60WD1", false, kWarningInterruption);
+}
+
+bool NoradDelta::playingAgainstRobot() {
+ return GameState.getNoradPlayedGlobeGame();
+}
+
+void NoradDelta::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID,
+ HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID,
+ HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) {
+ outSpotID = kNorad60MonitorOutSpotID;
+ prepSpotID = kNorad60LaunchPrepSpotID;
+ clawControlSpotID = kNorad60ClawControlSpotID;
+ pinchClawSpotID = kNorad60ClawPinchSpotID;
+ moveClawDownSpotID = kNorad60ClawDownSpotID;
+ moveClawRightSpotID = kNorad60ClawRightSpotID;
+ moveClawLeftSpotID = kNorad60ClawLeftSpotID;
+ moveClawUpSpotID = kNorad60ClawUpSpotID;
+ clawCCWSpotID = kNorad60ClawCCWSpotID;
+ clawCWSpotID = kNorad60ClawCWSpotID;
+ clawPosition = kClawAtC;
+ clawExtraIDs = _noradDeltaClawExtras;
+}
+
+void NoradDelta::playerBeatRobotWithDoor() {
+ GameState.setNoradBeatRobotWithDoor(true);
+ updateViewFrame();
+ GameState.setScoringStoppedNoradRobot(true);
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
+}
+
+void NoradDelta::playerBeatRobotWithClaw() {
+ GameState.setNoradBeatRobotWithClaw(true);
+ updateViewFrame();
+ GameState.setScoringStoppedNoradRobot(true);
+ GameState.setScoringNoradGandhi(true);
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
+}
+
+TimeValue NoradDelta::getViewTime(const RoomID room, const DirectionConstant direction) {
+ ExtraTable::Entry entry;
+
+ if (room == kNorad41 && direction == kSouth && !GameState.getNoradArrivedFromSub()) {
+ getExtraEntry(kArriveFromSubChase, entry);
+ return entry.movieStart;
+ }
+
+ if (GameState.getNoradBeatRobotWithDoor()) {
+ if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
+ uint32 extraID = kN59Biochips111;
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
+ extraID += 1;
+ if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
+ extraID += 2;
+ if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
+ extraID += 4;
+ getExtraEntry(extraID, entry);
+ return entry.movieStart;
+ }
+
+ getExtraEntry(kN59RobotHeadOpens, entry);
+ return entry.movieStart;
+ } else if (GameState.getNoradBeatRobotWithClaw()) {
+ if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
+ uint32 extraID = kN60Biochips111;
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
+ extraID += 1;
+ if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
+ extraID += 2;
+ if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
+ extraID += 4;
+ getExtraEntry(extraID, entry);
+ return entry.movieStart;
+ }
+
+ getExtraEntry(kN60RobotHeadOpens, entry);
+ return entry.movieStart;
+ }
+
+ return Norad::getViewTime(room, direction);
+}
+
+void NoradDelta::openDoor() {
+ if (GameState.getCurrentRoom() == kNorad59 && GameState.getCurrentDirection() == kWest && GameState.getNoradPlayedGlobeGame()) {
+ Input scratch;
+ InputHandler::_inputHandler->clickInHotspot(scratch, _vm->getAllHotspots().findHotspotByID(kNorad59WestSpotID));
+ } else {
+ Norad::openDoor();
+ }
+}
+
+void NoradDelta::activateHotspots() {
+ Norad::activateHotspots();
+
+ if (GameState.getCurrentRoom() == kNorad59West && GameState.getCurrentDirection() == kWest && GameState.getNoradBeatRobotWithDoor()) {
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad59WestOutSpotID);
+
+ if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
+ if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
+ _vm->getAllHotspots().activateOneHotspot(kDelta59RobotShieldBiochipSpotID);
+ else
+ _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotShieldBiochipSpotID);
+
+ if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
+ _vm->getAllHotspots().activateOneHotspot(kDelta59RobotOpMemBiochipSpotID);
+ else
+ _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotOpMemBiochipSpotID);
+
+ if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
+ _vm->getAllHotspots().activateOneHotspot(kDelta59RobotRetinalBiochipSpotID);
+ else
+ _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotRetinalBiochipSpotID);
+ } else
+ _vm->getAllHotspots().activateOneHotspot(kDelta59RobotHeadSpotID);
+ } else if (GameState.getCurrentRoom() == kNorad60West && GameState.getCurrentDirection() == kWest &&
+ GameState.getNoradBeatRobotWithClaw()) {
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad60MonitorOutSpotID);
+
+ if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
+ if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
+ _vm->getAllHotspots().activateOneHotspot(kDelta60RobotShieldBiochipSpotID);
+ else
+ _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotShieldBiochipSpotID);
+
+ if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
+ _vm->getAllHotspots().activateOneHotspot(kDelta60RobotOpMemBiochipSpotID);
+ else
+ _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotOpMemBiochipSpotID);
+
+ if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
+ _vm->getAllHotspots().activateOneHotspot(kDelta60RobotRetinalBiochipSpotID);
+ else
+ _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotRetinalBiochipSpotID);
+ } else {
+ _vm->getAllHotspots().activateOneHotspot(kDelta60RobotHeadSpotID);
+ }
+ } else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad50, kEast)) {
+ if (GameState.isCurrentDoorOpen())
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad50DoorSpotID);
+ } else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad59, kWest)) {
+ if (GameState.isCurrentDoorOpen())
+ _vm->getAllHotspots().deactivateOneHotspot(kNorad59WestSpotID);
+ }
+}
+
+void NoradDelta::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+ switch (clickedSpot->getObjectID()) {
+ case kDelta59RobotHeadSpotID:
+ startExtraSequence(kN59RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kDelta60RobotHeadSpotID:
+ startExtraSequence(kN60RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ Norad::clickInHotspot(input, clickedSpot);
+ break;
+ }
+}
+
+void NoradDelta::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ Norad::receiveNotification(notification, flags);
+
+ if ((flags & kExtraCompletedFlag) != 0) {
+ RetScanChip *retScan;
+ Input dummy;
+
+ switch (_lastExtra) {
+ case kArriveFromSubChase:
+ GameState.setNoradArrivedFromSub(true);
+ GameState.setCurrentRoom(kNoRoomID);
+ GameState.setCurrentDirection(kNoDirection);
+ arriveAt(kNorad41, kEast);
+ break;
+ case kN59RobotHeadOpens:
+ case kN60RobotHeadOpens:
+ _privateFlags.setFlag(kNoradPrivateRobotHeadOpenFlag, true);
+ break;
+ case kNoradDeltaRetinalScanBad:
+ retScan = (RetScanChip *)_vm->getCurrentBiochip();
+ retScan->setItemState(kNormalItem);
+ playSpotSoundSync(kRetinalScanFailedIn, kRetinalScanFailedOut);
+ downButton(dummy);
+ break;
+ case kNoradDeltaRetinalScanGood:
+ retScan = (RetScanChip *)_vm->getCurrentBiochip();
+ retScan->setItemState(kNormalItem);
+ downButton(dummy);
+ break;
+ case kN59RobotDisappears:
+ case kN60RobotDisappears:
+ recallToTSASuccess();
+ break;
+ }
+
+ _interruptionFilter = kFilterAllInput;
+ }
+
+ g_AIArea->checkMiddleArea();
+}
+
+void NoradDelta::pickedUpItem(Item *item) {
+ switch (item->getObjectID()) {
+ case kShieldBiochip:
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) {
+ GameState.setNoradFinished(true);
+
+ if (GameState.getCurrentRoom() == kNorad59West)
+ startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kRetinalScanBiochip:
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) {
+ GameState.setNoradFinished(true);
+
+ if (GameState.getCurrentRoom() == kNorad59West)
+ startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kOpticalBiochip:
+ g_opticalChip->addPoseidon();
+ GameState.setScoringGotNoradOpMemChip();
+
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) {
+ GameState.setNoradFinished(true);
+
+ if (GameState.getCurrentRoom() == kNorad59West)
+ startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ }
+
+ Norad::pickedUpItem(item);
+}
+
+void NoradDelta::takeItemFromRoom(Item *item) {
+ switch (item->getObjectID()) {
+ case kShieldBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, true);
+ break;
+ case kRetinalScanBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, true);
+ break;
+ case kOpticalBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, true);
+ break;
+ }
+
+ Norad::takeItemFromRoom(item);
+}
+
+void NoradDelta::dropItemIntoRoom(Item *item, Hotspot *hotspot) {
+ switch (item->getObjectID()) {
+ case kShieldBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, false);
+ break;
+ case kOpticalBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, false);
+ break;
+ case kRetinalScanBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, false);
+ break;
+ }
+
+ Norad::dropItemIntoRoom(item, hotspot);
+}
+
+Hotspot *NoradDelta::getItemScreenSpot(Item *item, DisplayElement *element) {
+ HotSpotID id = kNoHotSpotID;
+
+ switch (item->getObjectID()) {
+ case kShieldBiochip:
+ if (GameState.getNoradBeatRobotWithDoor())
+ id = kDelta59RobotShieldBiochipSpotID;
+ else
+ id = kDelta60RobotShieldBiochipSpotID;
+ break;
+ case kOpticalBiochip:
+ if (GameState.getNoradBeatRobotWithDoor())
+ id = kDelta59RobotOpMemBiochipSpotID;
+ else
+ id = kDelta60RobotOpMemBiochipSpotID;
+ break;
+ case kRetinalScanBiochip:
+ if (GameState.getNoradBeatRobotWithDoor())
+ id = kDelta59RobotRetinalBiochipSpotID;
+ else
+ id = kDelta60RobotRetinalBiochipSpotID;
+ break;
+ }
+
+ if (id != kNoHotSpotID)
+ return _vm->getAllHotspots().findHotspotByID(id);
+
+ return Norad::getItemScreenSpot(item, element);
+}
+
+Common::String NoradDelta::getEnvScanMovie() {
+ return "Images/AI/Norad/XNE2";
+}
+
+uint NoradDelta::getNumHints() {
+ uint numHints = Neighborhood::getNumHints();
+
+ if (numHints == 0) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kNorad60, kWest):
+ if (GameState.getNoradPlayedGlobeGame())
+ numHints = 2;
+ else
+ numHints = 1;
+ break;
+ case MakeRoomView(kNorad59, kNorth):
+ case MakeRoomView(kNorad59, kSouth):
+ case MakeRoomView(kNorad59, kEast):
+ case MakeRoomView(kNorad59, kWest):
+ case MakeRoomView(kNorad60, kNorth):
+ case MakeRoomView(kNorad60, kSouth):
+ case MakeRoomView(kNorad60, kEast):
+ if (GameState.getNoradPlayedGlobeGame())
+ numHints = 2;
+ break;
+ case MakeRoomView(kNorad68, kWest):
+ if (_vm->playerHasItemID(kRetinalScanBiochip)) {
+ BiochipItem *retScan = _vm->getCurrentBiochip();
+ if (retScan == 0 || retScan->getObjectID() != kRetinalScanBiochip)
+ numHints = 2;
+ } else if (!GameState.isCurrentDoorOpen()) {
+ numHints = 2;
+ }
+ break;
+ }
+ }
+
+ return numHints;
+}
+
+Common::String NoradDelta::getHintMovie(uint hintNum) {
+ Common::String movieName = Neighborhood::getHintMovie(hintNum);
+
+ if (movieName.empty()) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kNorad60, kWest):
+ if (GameState.getNoradPlayedGlobeGame()) {
+ if (hintNum == 1)
+ return "Images/AI/Norad/XN60WD2";
+
+ return "Images/AI/Norad/XN60WD3";
+ }
+
+ return "Images/AI/Globals/XGLOB1C";
+ case MakeRoomView(kNorad59, kNorth):
+ case MakeRoomView(kNorad59, kSouth):
+ case MakeRoomView(kNorad59, kEast):
+ case MakeRoomView(kNorad59, kWest):
+ case MakeRoomView(kNorad60, kNorth):
+ case MakeRoomView(kNorad60, kSouth):
+ case MakeRoomView(kNorad60, kEast):
+ if (hintNum == 1)
+ return "Images/AI/Norad/XN60WD2";
+
+ return "Images/AI/Norad/XN60WD3";
+ case MakeRoomView(kNorad68, kWest):
+ if (_vm->playerHasItemID(kRetinalScanBiochip)) {
+ if (hintNum == 1)
+ return "Images/AI/Globals/XGLOB1A";
+
+ return "Images/AI/Globals/XGLOB1C";
+ }
+
+ if (hintNum == 1)
+ return "Images/AI/Globals/XGLOB1B";
+
+ return "Images/AI/Globals/XGLOB3B";
+ }
+ }
+
+ return movieName;
+}
+
+void NoradDelta::closeDoorOffScreen(const RoomID room, const DirectionConstant) {
+ switch (room) {
+ case kNorad47:
+ case kNorad48:
+ case kNorad41:
+ case kNorad42:
+ playSpotSoundSync(kDeltaElevatorDoorCloseIn, kDeltaElevatorDoorCloseOut);
+ break;
+ default:
+ playSpotSoundSync(kDeltaRegDoorCloseIn, kDeltaRegDoorCloseOut);
+ break;
+ }
+}
+
+bool NoradDelta::canSolve() {
+ if (Norad::canSolve())
+ return true;
+
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) {
+ BiochipItem *biochip = _vm->getCurrentBiochip();
+ if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip)
+ return true;
+ }
+
+ return false;
+}
+
+void NoradDelta::doSolve() {
+ Norad::doSolve();
+
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) {
+ if (!_vm->playerHasItemID(kRetinalScanBiochip))
+ _vm->addItemToBiochips((BiochipItem *)_vm->getAllItems().findItemByID(kRetinalScanBiochip));
+
+ BiochipItem *biochip = _vm->getCurrentBiochip();
+ if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip && g_interface)
+ g_interface->setCurrentBiochipID(kRetinalScanBiochip);
+
+ Hotspot *spot = _vm->getAllHotspots().findHotspotByID(kNorad68WestSpotID);
+ Input scratch;
+ InputHandler::_inputHandler->clickInHotspot(scratch, spot);
+ }
+}
+
+Common::String NoradDelta::getSoundSpotsName() {
+ return "Sounds/Norad/Norad Delta Spots";
+}
+
+Common::String NoradDelta::getNavMovieName() {
+ return "Images/Norad Delta/Norad Delta.movie";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.h b/engines/pegasus/neighborhood/norad/delta/noraddelta.h
new file mode 100644
index 0000000000..11065f2c9d
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.h
@@ -0,0 +1,117 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H
+
+#include "pegasus/neighborhood/norad/norad.h"
+
+namespace Pegasus {
+
+class NoradDelta : public Norad {
+public:
+ NoradDelta(InputHandler *, PegasusEngine *);
+ virtual ~NoradDelta() {}
+
+ void init();
+
+ void start();
+
+ void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &);
+
+ void finishedGlobeGame();
+
+ virtual GameInteraction *makeInteraction(const InteractionID);
+
+ void playClawMonitorIntro();
+
+ virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID,
+ HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID,
+ HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID,
+ HotSpotID &clawCWSpotID, uint32 &, const uint32 *&);
+
+ void playerBeatRobotWithClaw();
+ void playerBeatRobotWithDoor();
+
+ void loadAmbientLoops();
+
+ void setUpAIRules();
+ Common::String getEnvScanMovie();
+ uint getNumHints();
+ Common::String getHintMovie(uint);
+ void closeDoorOffScreen(const RoomID, const DirectionConstant);
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+ bool canSolve();
+ void doSolve();
+
+ void doorOpened();
+
+protected:
+ enum {
+ kNoradPrivateArrivedFromSubFlag,
+ kNoradPrivateFinishedGlobeGameFlag,
+ kNoradPrivateRobotHeadOpenFlag,
+ kNoradPrivateGotShieldChipFlag,
+ kNoradPrivateGotOpticalChipFlag,
+ kNoradPrivateGotRetScanChipFlag,
+ kNumNoradPrivateFlags
+ };
+
+ static const uint32 _noradDeltaClawExtras[22];
+
+ void getExitEntry(const RoomID, const DirectionConstant, ExitTable::Entry &);
+ void getZoomEntry(const HotSpotID, ZoomTable::Entry &);
+ virtual void arriveAt(const RoomID, const DirectionConstant);
+ void arriveAtNorad68West();
+ void arriveAtNorad79West();
+ TimeValue getViewTime(const RoomID, const DirectionConstant);
+ void openDoor();
+ void activateHotspots();
+ void clickInHotspot(const Input &, const Hotspot *);
+ void receiveNotification(Notification *, const NotificationFlags);
+ void pickedUpItem(Item *item);
+ void takeItemFromRoom(Item *item);
+ void dropItemIntoRoom(Item *item, Hotspot *);
+ Hotspot *getItemScreenSpot(Item *, DisplayElement *);
+
+ virtual bool playingAgainstRobot();
+
+ void failRetinalScan();
+ void succeedRetinalScan();
+ void getDoorEntry(const RoomID, const DirectionConstant, DoorTable::Entry &);
+
+ void bumpIntoWall();
+
+ FlagsArray<byte, kNumNoradPrivateFlags> _privateFlags;
+
+ Common::String getSoundSpotsName();
+ Common::String getNavMovieName();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/norad.cpp b/engines/pegasus/neighborhood/norad/norad.cpp
new file mode 100644
index 0000000000..578f062dea
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/norad.cpp
@@ -0,0 +1,285 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/noradelevator.h"
+#include "pegasus/neighborhood/norad/pressuredoor.h"
+#include "pegasus/neighborhood/norad/subcontrolroom.h"
+#include "pegasus/neighborhood/norad/subplatform.h"
+
+namespace Pegasus {
+
+const NotificationFlags kDoneWithPressureDoorNotification = 1;
+
+const NotificationFlags kNoradNotificationFlags = kDoneWithPressureDoorNotification;
+
+// This class handles everything that Norad Alpha and Delta have in common, such as
+// oxygen mask usage, the elevator and the pressure doors.
+
+Norad::Norad(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) :
+ Neighborhood(nextHandler, vm, resName, id), _noradNotification(kNoradNotificationID, vm) {
+ _elevatorUpSpotID = kNoHotSpotID;
+ _elevatorDownSpotID = kNoHotSpotID;
+ _elevatorUpRoomID = kNoHotSpotID;
+ _elevatorDownRoomID = kNoHotSpotID;
+
+ _subRoomEntryRoom1 = kNoRoomID;
+ _subRoomEntryDir1 = kNoDirection;
+ _subRoomEntryRoom2 = kNoRoomID;
+ _subRoomEntryDir2 = kNoDirection;
+ _upperPressureDoorRoom = kNoRoomID;
+ _lowerPressureDoorRoom = kNoRoomID;
+
+ _upperPressureDoorUpSpotID = kNoHotSpotID;
+ _upperPressureDoorDownSpotID = kNoHotSpotID;
+ _upperPressureDoorAbortSpotID = kNoHotSpotID;
+
+ _lowerPressureDoorUpSpotID = kNoHotSpotID;
+ _lowerPressureDoorDownSpotID = kNoHotSpotID;
+ _lowerPressureDoorAbortSpotID = kNoHotSpotID;
+
+ _pressureSoundIn = 0xffffffff;
+ _pressureSoundOut = 0xffffffff;
+ _equalizeSoundIn = 0xffffffff;
+ _equalizeSoundOut = 0xffffffff;
+ _accessDeniedIn = 0xffffffff;
+ _accessDeniedOut = 0xffffffff;
+
+ _platformRoom = kNoRoomID;
+ _subControlRoom = kNoRoomID;
+
+ _doneWithPressureDoor = false;
+
+ _noradNotification.notifyMe(this, kNoradNotificationFlags, kNoradNotificationFlags);
+}
+
+GameInteraction *Norad::makeInteraction(const InteractionID interactionID) {
+ PressureDoor *pressureDoor;
+ SubControlRoom *subControl;
+
+ switch (interactionID) {
+ case kNoradElevatorInteractionID:
+ return new NoradElevator(this, _elevatorUpRoomID, _elevatorDownRoomID, _elevatorUpSpotID, _elevatorDownSpotID);
+ case kNoradPressureDoorInteractionID:
+ if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
+ pressureDoor = new PressureDoor(this, true, _upperPressureDoorUpSpotID, _upperPressureDoorDownSpotID,
+ _upperPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
+ else
+ pressureDoor = new PressureDoor(this, false, _lowerPressureDoorUpSpotID, _lowerPressureDoorDownSpotID,
+ _lowerPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
+
+ if (GameState.getCurrentRoom() == kNorad59West && playingAgainstRobot())
+ pressureDoor->playAgainstRobot();
+
+ return pressureDoor;
+ case kNoradSubControlRoomInteractionID:
+ subControl = new SubControlRoom(this);
+
+ if (GameState.getCurrentRoom() == kNorad60West && playingAgainstRobot())
+ subControl->playAgainstRobot();
+
+ return subControl;
+ case kNoradSubPlatformInteractionID:
+ return new SubPlatform(this);
+ default:
+ return 0;
+ }
+}
+
+void Norad::flushGameState() {
+ g_energyMonitor->saveCurrentEnergyValue();
+}
+
+void Norad::start() {
+ setUpAirMask();
+ Neighborhood::start();
+}
+
+void Norad::activateHotspots() {
+ Neighborhood::activateHotspots();
+
+ RoomID room = GameState.getCurrentRoom();
+ if (room == _elevatorUpRoomID)
+ _neighborhoodHotspots.activateOneHotspot(_elevatorDownSpotID);
+ else if (room == _elevatorDownRoomID)
+ _neighborhoodHotspots.activateOneHotspot(_elevatorUpSpotID);
+}
+
+void Norad::arriveAt(const RoomID room, const DirectionConstant direction) {
+ Neighborhood::arriveAt(room, direction);
+
+ if (GameState.getCurrentRoom() == _elevatorUpRoomID || GameState.getCurrentRoom() == _elevatorDownRoomID)
+ arriveAtNoradElevator();
+ else if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
+ arriveAtUpperPressureDoorRoom();
+ else if (GameState.getCurrentRoom() == _lowerPressureDoorRoom)
+ arriveAtLowerPressureDoorRoom();
+ else if (GameState.getCurrentRoom() == _platformRoom)
+ arriveAtSubPlatformRoom();
+ else if (GameState.getCurrentRoom() == _subControlRoom)
+ arriveAtSubControlRoom();
+
+ if (_doneWithPressureDoor) {
+ _doneWithPressureDoor = false;
+ openDoor();
+ }
+}
+
+void Norad::arriveAtNoradElevator() {
+ if (_currentInteraction)
+ _currentInteraction->startOverInteraction();
+ else
+ newInteraction(kNoradElevatorInteractionID);
+}
+
+void Norad::arriveAtUpperPressureDoorRoom() {
+ newInteraction(kNoradPressureDoorInteractionID);
+}
+
+void Norad::arriveAtLowerPressureDoorRoom() {
+ newInteraction(kNoradPressureDoorInteractionID);
+}
+
+void Norad::arriveAtSubPlatformRoom() {
+ newInteraction(kNoradSubPlatformInteractionID);
+}
+
+void Norad::arriveAtSubControlRoom() {
+ newInteraction(kNoradSubControlRoomInteractionID);
+}
+
+int16 Norad::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
+ int16 result = Neighborhood::getStaticCompassAngle(room, dir);
+
+ if (room == _elevatorUpRoomID || room == _elevatorDownRoomID)
+ result += kElevatorCompassAngle;
+ else if (room == _platformRoom)
+ result += kSubPlatformCompassAngle;
+ else if (room == _subControlRoom)
+ result += kSubControlCompassAngle;
+
+ return result;
+}
+
+CanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) {
+ if (((GameState.getCurrentRoom() == _subRoomEntryRoom1 && GameState.getCurrentDirection() == _subRoomEntryDir1) ||
+ (GameState.getCurrentRoom() == _subRoomEntryRoom2 && GameState.getCurrentDirection() == _subRoomEntryDir2)) &&
+ GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure)
+ return kCantOpenBadPressure;
+
+ return Neighborhood::canOpenDoor(entry);
+}
+
+void Norad::cantOpenDoor(CanOpenDoorReason reason) {
+ if (reason == kCantOpenBadPressure)
+ playSpotSoundSync(_pressureSoundIn, _pressureSoundOut);
+ else
+ playSpotSoundSync(_accessDeniedIn, _accessDeniedOut);
+}
+
+void Norad::startExitMovie(const ExitTable::Entry &exitEntry) {
+ if (GameState.getCurrentRoom() == _elevatorUpRoomID) {
+ if (exitEntry.exitRoom != _elevatorDownRoomID)
+ newInteraction(kNoInteractionID);
+ } else if (GameState.getCurrentRoom() == _elevatorDownRoomID) {
+ if (exitEntry.exitRoom != _elevatorUpRoomID)
+ newInteraction(kNoInteractionID);
+ } else {
+ newInteraction(kNoInteractionID);
+ }
+
+ Neighborhood::startExitMovie(exitEntry);
+}
+
+void Norad::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
+ newInteraction(kNoInteractionID);
+ Neighborhood::startZoomMovie(zoomEntry);
+}
+
+void Norad::upButton(const Input &input) {
+ if (GameState.getCurrentRoom() != _elevatorUpRoomID && GameState.getCurrentRoom() != _elevatorDownRoomID)
+ Neighborhood::upButton(input);
+}
+
+void Norad::setUpAirMask() {
+ _airMaskCallBack.setNotification(&_neighborhoodNotification);
+ _airMaskCallBack.initCallBack(&_airMaskTimer, kCallBackAtExtremes);
+ _airMaskCallBack.setCallBackFlag(kAirTimerExpiredFlag);
+ _neighborhoodNotification.notifyMe(this, kAirTimerExpiredFlag, kAirTimerExpiredFlag);
+ _airMaskCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _airMaskTimer.setScale(1);
+ _airMaskTimer.setSegment(0, kNoradAirMaskTimeLimit);
+ checkAirMask();
+}
+
+void Norad::checkAirMask() {
+ if (g_airMask && g_airMask->isAirFilterOn()) {
+ _airMaskTimer.stop();
+ } else if (GameState.getNoradGassed() && !_airMaskTimer.isRunning()) {
+ _airMaskTimer.setTime(0);
+ _airMaskTimer.start();
+ }
+
+ loadAmbientLoops();
+}
+
+void Norad::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ if (notification == &_neighborhoodNotification && (flags & kAirTimerExpiredFlag) != 0)
+ ((PegasusEngine *)g_engine)->die(kDeathGassedInNorad);
+
+ Neighborhood::receiveNotification(notification, flags);
+
+ if (notification == &_noradNotification) {
+ // Must be kDoneWithPressureDoorNotification...
+ Input scratch;
+ _doneWithPressureDoor = true;
+ downButton(scratch);
+ }
+}
+
+uint16 Norad::getDateResID() const {
+ return kDate2112ID;
+}
+
+Common::String Norad::getBriefingMovie() {
+ return "Images/AI/Norad/XNO";
+}
+
+void Norad::pickedUpItem(Item *item) {
+ Neighborhood::pickedUpItem(item);
+ g_AIArea->checkMiddleArea();
+}
+
+void Norad::doneWithPressureDoor() {
+ _noradNotification.setNotificationFlags(kDoneWithPressureDoorNotification, kDoneWithPressureDoorNotification);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/norad.h b/engines/pegasus/neighborhood/norad/norad.h
new file mode 100644
index 0000000000..3cd77cc54b
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/norad.h
@@ -0,0 +1,121 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H
+
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+// This is the code common to both Norad Alpha and Norad Delta
+
+class Norad : public Neighborhood {
+public:
+ Norad(InputHandler *, PegasusEngine *owner, const Common::String &resName, const NeighborhoodID);
+ virtual ~Norad() {}
+
+ void flushGameState();
+
+ virtual void start();
+
+ virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID,
+ HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID,
+ HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID,
+ HotSpotID &moveClawLeftSpotID,HotSpotID &moveClawUpSpotID,
+ HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &, const uint32 *&) = 0;
+ void checkAirMask();
+
+ virtual uint16 getDateResID() const;
+
+ virtual GameInteraction *makeInteraction(const InteractionID);
+
+ Common::String getBriefingMovie();
+
+ void pickedUpItem(Item *);
+
+ virtual void playClawMonitorIntro() {}
+
+ void doneWithPressureDoor();
+
+protected:
+ CanOpenDoorReason canOpenDoor(DoorTable::Entry &);
+ void cantOpenDoor(CanOpenDoorReason);
+ int16 getStaticCompassAngle(const RoomID, const DirectionConstant);
+ virtual void startExitMovie(const ExitTable::Entry &);
+ void startZoomMovie(const ZoomTable::Entry &);
+ virtual void upButton(const Input &);
+ virtual void activateHotspots();
+
+ virtual void arriveAt(const RoomID, const DirectionConstant);
+ virtual void arriveAtNoradElevator();
+ virtual void arriveAtUpperPressureDoorRoom();
+ virtual void arriveAtLowerPressureDoorRoom();
+ virtual void arriveAtSubPlatformRoom();
+ virtual void arriveAtSubControlRoom();
+ void setUpAirMask();
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+ virtual bool playingAgainstRobot() { return false; }
+
+ Notification _noradNotification;
+ bool _doneWithPressureDoor;
+
+ RoomID _elevatorUpRoomID;
+ RoomID _elevatorDownRoomID;
+ HotSpotID _elevatorUpSpotID;
+ HotSpotID _elevatorDownSpotID;
+
+ TimeBase _airMaskTimer;
+ NotificationCallBack _airMaskCallBack;
+
+ RoomID _subRoomEntryRoom1;
+ DirectionConstant _subRoomEntryDir1;
+ RoomID _subRoomEntryRoom2;
+ DirectionConstant _subRoomEntryDir2;
+ RoomID _upperPressureDoorRoom;
+ RoomID _lowerPressureDoorRoom;
+
+ HotSpotID _upperPressureDoorUpSpotID;
+ HotSpotID _upperPressureDoorDownSpotID;
+ HotSpotID _upperPressureDoorAbortSpotID;
+
+ HotSpotID _lowerPressureDoorUpSpotID;
+ HotSpotID _lowerPressureDoorDownSpotID;
+ HotSpotID _lowerPressureDoorAbortSpotID;
+
+ TimeValue _pressureSoundIn;
+ TimeValue _pressureSoundOut;
+ TimeValue _equalizeSoundIn;
+ TimeValue _equalizeSoundOut;
+ TimeValue _accessDeniedIn;
+ TimeValue _accessDeniedOut;
+
+ RoomID _platformRoom;
+ RoomID _subControlRoom;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/noradelevator.cpp b/engines/pegasus/neighborhood/norad/noradelevator.cpp
new file mode 100644
index 0000000000..1751f4dcb6
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/noradelevator.cpp
@@ -0,0 +1,130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/noradelevator.h"
+
+namespace Pegasus {
+
+// Norad elevator PICTs:
+static const ResIDType kElevatorLabelID = 200;
+static const ResIDType kElevatorButtonsID = 201;
+static const ResIDType kElevatorDownOnID = 202;
+static const ResIDType kElevatorUpOnID = 203;
+
+NoradElevator::NoradElevator(Neighborhood *handler, const RoomID upRoom, const RoomID downRoom,
+ const HotSpotID upHotspot, const HotSpotID downHotspot) : GameInteraction(kNoradElevatorInteractionID, handler),
+ _elevatorControls(kNoradElevatorControlsID), _elevatorNotification(kNoradElevatorNotificationID, ((PegasusEngine *)g_engine)) {
+ _timerExpired = false;
+ _upRoom = upRoom;
+ _downRoom = downRoom;
+ _upHotspot = upHotspot;
+ _downHotspot = downHotspot;
+}
+
+void NoradElevator::openInteraction() {
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorLabelID, true);
+ _elevatorControls.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorButtonsID, true);
+ _elevatorControls.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorDownOnID, true);
+ _elevatorControls.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorUpOnID, true);
+ _elevatorControls.addFrame(frame, 0, 0);
+
+ _elevatorControls.setCurrentFrameIndex(0);
+ _elevatorControls.setDisplayOrder(kElevatorControlsOrder);
+
+ Common::Rect r;
+ frame->getSurfaceBounds(r);
+ r.moveTo(kNoradAlphaElevatorControlsLeft, kNoradAlphaElevatorControlsTop);
+
+ _elevatorControls.setBounds(r);
+ _elevatorControls.startDisplaying();
+ _elevatorControls.show();
+}
+
+void NoradElevator::initInteraction() {
+ _elevatorTimer.setScale(2);
+ _elevatorTimer.setSegment(0, 1);
+ _elevatorCallBack.initCallBack(&_elevatorTimer, kCallBackAtExtremes);
+ _elevatorCallBack.setCallBackFlag(1);
+ _elevatorCallBack.setNotification(&_elevatorNotification);
+ _elevatorNotification.notifyMe(this, 1, 1);
+ _elevatorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _elevatorTimer.start();
+}
+
+void NoradElevator::closeInteraction() {
+ _elevatorControls.stopDisplaying();
+ _elevatorControls.discardFrames();
+ _elevatorCallBack.releaseCallBack();
+}
+
+void NoradElevator::resetInteraction() {
+ _elevatorControls.setCurrentFrameIndex(1);
+}
+
+void NoradElevator::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ if (_timerExpired) {
+ if (GameState.getCurrentRoom() == _upRoom)
+ g_allHotspots.activateOneHotspot(_downHotspot);
+ else if (GameState.getCurrentRoom() == _downRoom)
+ g_allHotspots.activateOneHotspot(_upHotspot);
+ }
+}
+
+void NoradElevator::clickInHotspot(const Input &input, const Hotspot *spot) {
+ HotSpotID id = spot->getObjectID();
+
+ if (id == _upHotspot || id == _downHotspot) {
+ g_neighborhood->moveForward();
+ if (id == _downHotspot)
+ _elevatorControls.setCurrentFrameIndex(2);
+ else
+ _elevatorControls.setCurrentFrameIndex(3);
+ } else {
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+void NoradElevator::receiveNotification(Notification *, const NotificationFlags) {
+ _elevatorControls.setCurrentFrameIndex(1);
+ _timerExpired = true;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/noradelevator.h b/engines/pegasus/neighborhood/norad/noradelevator.h
new file mode 100644
index 0000000000..24aa488534
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/noradelevator.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/notification.h"
+#include "pegasus/surface.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class Neighborhood;
+
+class NoradElevator : public GameInteraction, private NotificationReceiver {
+public:
+ NoradElevator(Neighborhood *, const RoomID, const RoomID, const HotSpotID, const HotSpotID);
+ virtual ~NoradElevator() {}
+
+protected:
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+ virtual void resetInteraction();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+
+ RoomID _upRoom;
+ RoomID _downRoom;
+ HotSpotID _upHotspot;
+ HotSpotID _downHotspot;
+ Sprite _elevatorControls;
+ TimeBase _elevatorTimer;
+ NotificationCallBack _elevatorCallBack;
+ Notification _elevatorNotification;
+ bool _timerExpired;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.cpp b/engines/pegasus/neighborhood/norad/pressuredoor.cpp
new file mode 100644
index 0000000000..d1378567d3
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/pressuredoor.cpp
@@ -0,0 +1,554 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/pressuredoor.h"
+#include "pegasus/neighborhood/norad/delta/noraddelta.h"
+
+namespace Pegasus {
+
+static const TimeValue kLevelsSplashStart = 0;
+static const TimeValue kLevelsSplashStop = 1;
+static const TimeValue kPressureBase = 1;
+
+static const TimeValue kDoorSealedTime = 0;
+static const TimeValue kEqualizeTime = 1;
+static const TimeValue kMaxPressureLoopStart = 2;
+static const TimeValue kMaxPressureLoopStop = 3;
+static const TimeValue kOpeningDoorLoopStart = 3;
+static const TimeValue kOpeningDoorLoopStop = 4;
+static const TimeValue kIncreasingPressureTime = 4;
+static const TimeValue kDecreasingPressureTime = 5;
+static const TimeValue kCautionLoopStart = 6;
+static const TimeValue kCautionLoopStop = 7;
+
+static const NotificationFlags kSplashFinished = 1;
+static const NotificationFlags kPressureDroppingFlag = kSplashFinished << 1;
+
+static const NotificationFlags kPressureNotificationFlags = kSplashFinished |
+ kPressureDroppingFlag;
+
+static const NotificationFlags kDoorJumpsUpFlag = 1;
+static const NotificationFlags kDoorJumpsBackFlag = kDoorJumpsUpFlag << 1;
+static const NotificationFlags kDoorCrushedFlag = kDoorJumpsBackFlag << 1;
+
+static const NotificationFlags kUtilityNotificationFlags = kDoorJumpsUpFlag |
+ kDoorJumpsBackFlag |
+ kDoorCrushedFlag;
+
+enum {
+ kPlayingRobotApproaching,
+ kRobotPunching,
+ kRobotComingThrough,
+ kRobotDying,
+ kRobotDead
+};
+
+const short kMaxPunches = 5;
+
+enum {
+ kPlayingSplash,
+ kPlayingPressureMessage,
+ kPlayingEqualizeMessage,
+ kWaitingForPlayer,
+ kPlayingDoneMessage,
+ kGameOver
+};
+
+// Pressure values range from 0 to 11.
+static const short kMinPressure = 0;
+static const short kMaxPressure = 11;
+
+static const TimeScale kNavTimeScale = 600;
+static const TimeValue kNavFrameRate = 15;
+static const TimeValue kNavTimePerFrame = kNavTimeScale / kNavFrameRate;
+
+static const TimeValue kApproachPunchInTime = 122 * kNavTimePerFrame;
+static const TimeValue kLoopPunchInTime = 38 * kNavTimePerFrame;
+static const TimeValue kPunchThroughTime = 38 * kNavTimePerFrame;
+
+// Pressure door PICTs:
+static const ResIDType kUpperPressureUpOffPICTID = 400;
+static const ResIDType kUpperPressureUpOnPICTID = 401;
+static const ResIDType kUpperPressureDownOffPICTID = 402;
+static const ResIDType kUpperPressureDownOnPICTID = 403;
+
+static const ResIDType kLowerPressureUpOffPICTID = 404;
+static const ResIDType kLowerPressureUpOnPICTID = 405;
+static const ResIDType kLowerPressureDownOffPICTID = 406;
+static const ResIDType kLowerPressureDownOnPICTID = 407;
+
+PressureDoor::PressureDoor(Neighborhood *handler, bool isUpperDoor, const HotSpotID upSpotID, const HotSpotID downSpotID,
+ const HotSpotID outSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, TimeValue equalizeSoundIn,
+ TimeValue equalizeSoundOut) : GameInteraction(kNoradPressureDoorInteractionID, handler),
+ _levelsMovie(kPressureDoorLevelsID), _typeMovie(kPressureDoorTypeID), _upButton(kPressureDoorUpButtonID),
+ _downButton(kPressureDoorDownButtonID), _pressureNotification(kNoradPressureNotificationID, ((PegasusEngine *)g_engine)),
+ _doorTracker(this), _utilityNotification(kNoradUtilityNotificationID, ((PegasusEngine *)g_engine)) {
+ _neighborhoodNotification = handler->getNeighborhoodNotification();
+ _upHotspotID = upSpotID;
+ _downHotspotID = downSpotID;
+ _outHotspotID = outSpotID;
+ _pressureSoundIn = pressureSoundIn;
+ _pressureSoundOut = pressureSoundOut;
+ _equalizeSoundIn = equalizeSoundIn;
+ _equalizeSoundOut = equalizeSoundOut;
+ _playingAgainstRobot = false;
+ _isUpperDoor = isUpperDoor;
+}
+
+void PressureDoor::openInteraction() {
+ if (_isUpperDoor) {
+ _levelsMovie.initFromMovieFile("Images/Norad Alpha/Upper Levels Movie");
+ _levelsMovie.moveElementTo(kNoradUpperLevelsLeft, kNoradUpperLevelsTop);
+ } else {
+ _levelsMovie.initFromMovieFile("Images/Norad Alpha/Lower Levels Movie");
+ _levelsMovie.moveElementTo(kNoradLowerLevelsLeft, kNoradLowerLevelsTop);
+ }
+
+ _levelsScale = _levelsMovie.getScale();
+ _levelsMovie.setDisplayOrder(kPressureLevelsOrder);
+ _levelsMovie.startDisplaying();
+ _levelsMovie.setSegment(kLevelsSplashStart * _levelsScale, kLevelsSplashStop * _levelsScale);
+ _levelsMovie.setTime(kLevelsSplashStart * _levelsScale);
+ _levelsMovie.redrawMovieWorld();
+ _levelsMovie.show();
+
+ _pressureCallBack.setNotification(&_pressureNotification);
+ _pressureCallBack.initCallBack(&_levelsMovie, kCallBackAtExtremes);
+ _pressureCallBack.setCallBackFlag(kSplashFinished);
+ _pressureCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ _pressureNotification.notifyMe(this, kPressureNotificationFlags, kPressureNotificationFlags);
+
+ if (_isUpperDoor) {
+ _typeMovie.initFromMovieFile("Images/Norad Alpha/Upper Type Movie");
+ _typeMovie.moveElementTo(kNoradUpperTypeLeft, kNoradUpperTypeTop);
+ } else {
+ _typeMovie.initFromMovieFile("Images/Norad Alpha/Lower Type Movie");
+ _typeMovie.moveElementTo(kNoradLowerTypeLeft, kNoradLowerTypeTop);
+ }
+
+ _typeScale = _typeMovie.getScale();
+ _typeMovie.setDisplayOrder(kPressureTypeOrder);
+ _typeMovie.startDisplaying();
+ _typeMovie.setTime(kDoorSealedTime * _typeScale);
+ _typeMovie.redrawMovieWorld();
+
+ SpriteFrame *frame = new SpriteFrame();
+ if (_isUpperDoor)
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOffPICTID);
+ else
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOffPICTID);
+ _upButton.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ if (_isUpperDoor)
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOnPICTID);
+ else
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOnPICTID);
+ _upButton.addFrame(frame, 0, 0);
+
+ _upButton.setCurrentFrameIndex(0);
+ _upButton.setDisplayOrder(kPressureUpOrder);
+
+ Common::Rect r;
+ frame->getSurfaceBounds(r);
+ if (_isUpperDoor)
+ r.moveTo(kNoradUpperUpLeft, kNoradUpperUpTop);
+ else
+ r.moveTo(kNoradLowerUpLeft, kNoradLowerUpTop);
+
+ _upButton.setBounds(r);
+ _upButton.startDisplaying();
+ _upButton.show();
+
+ frame = new SpriteFrame();
+ if (_isUpperDoor)
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOffPICTID);
+ else
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOffPICTID);
+ _downButton.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ if (_isUpperDoor)
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOnPICTID);
+ else
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOnPICTID);
+ _downButton.addFrame(frame, 0, 0);
+
+ _downButton.setCurrentFrameIndex(0);
+ _downButton.setDisplayOrder(kPressureDownOrder);
+
+ frame->getSurfaceBounds(r);
+ if (_isUpperDoor)
+ r.moveTo(kNoradUpperDownLeft, kNoradUpperDownTop);
+ else
+ r.moveTo(kNoradLowerDownLeft, kNoradLowerDownTop);
+
+ _downButton.setBounds(r);
+ _downButton.startDisplaying();
+ _downButton.show();
+
+ _utilityCallBack.setNotification(&_utilityNotification);
+ _utilityCallBack.initCallBack(&_utilityTimer, kCallBackAtTime);
+ _utilityNotification.notifyMe(this, kUtilityNotificationFlags, kUtilityNotificationFlags);
+ _utilityTimer.setMasterTimeBase(getOwner()->getNavMovie());
+
+ if (_playingAgainstRobot)
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag | kDelayCompletedFlag |
+ kSpotSoundCompletedFlag, kExtraCompletedFlag | kDelayCompletedFlag | kSpotSoundCompletedFlag);
+ else
+ _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag,
+ kDelayCompletedFlag | kSpotSoundCompletedFlag);
+
+ _gameState = kPlayingSplash;
+}
+
+void PressureDoor::initInteraction() {
+ _levelsMovie.start();
+
+ if (_playingAgainstRobot) {
+ ExtraTable::Entry entry;
+ _owner->getExtraEntry(kN59RobotApproaches, entry);
+ _utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
+ _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
+ _punchInTime = kApproachPunchInTime + entry.movieStart;
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
+ _utilityTimer.setTime(entry.movieStart);
+ _owner->startExtraSequence(kN59RobotApproaches, kExtraCompletedFlag, kFilterAllInput);
+ _utilityTimer.start();
+ _robotState = kPlayingRobotApproaching;
+ }
+
+ _levelsMovie.redrawMovieWorld();
+}
+
+void PressureDoor::closeInteraction() {
+ _pressureNotification.cancelNotification(this);
+ _pressureCallBack.releaseCallBack();
+ _utilityNotification.cancelNotification(this);
+ _utilityCallBack.releaseCallBack();
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void PressureDoor::playAgainstRobot() {
+ _playingAgainstRobot = true;
+}
+
+void PressureDoor::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ Neighborhood *owner = getOwner();
+
+ if (notification == _neighborhoodNotification) {
+ if (_playingAgainstRobot && (flags & kExtraCompletedFlag) != 0) {
+ ExtraTable::Entry entry;
+
+ switch (_robotState) {
+ case kPlayingRobotApproaching:
+ _utilityTimer.stop();
+ if (GameState.getNoradSubRoomPressure() == kMaxPressure) {
+ owner->getExtraEntry(kN59PlayerWins1, entry);
+ _utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
+ _utilityTimer.setTime(entry.movieStart);
+ _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
+ _punchInTime = kLoopPunchInTime + entry.movieStart;
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
+ owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput);
+ _utilityTimer.start();
+ _robotState = kRobotDying;
+ } else {
+ owner->getExtraEntry(kN59RobotPunchLoop, entry);
+ _utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
+ _utilityTimer.setTime(entry.movieStart);
+ _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
+ _punchInTime = kLoopPunchInTime + entry.movieStart;
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
+ owner->startSpotLoop(entry.movieStart, entry.movieEnd, kExtraCompletedFlag);
+ _utilityTimer.start();
+ _robotState = kRobotPunching;
+ _punchCount = 1;
+ }
+ break;
+ case kRobotPunching:
+ if (GameState.getNoradSubRoomPressure() == kMaxPressure) {
+ owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput);
+ _robotState = kRobotDying;
+ } else if (++_punchCount >= kMaxPunches) {
+ _robotState = kRobotComingThrough;
+ owner->getExtraEntry(kN59RobotWins, entry);
+ _utilityTimer.stop();
+ _utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
+ _utilityTimer.setTime(entry.movieStart);
+ _utilityCallBack.cancelCallBack();
+ _utilityCallBack.setCallBackFlag(kDoorCrushedFlag);
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, kPunchThroughTime + entry.movieStart, kNavTimeScale);
+ owner->startExtraSequence(kN59RobotWins, kExtraCompletedFlag, kFilterNoInput);
+ _utilityTimer.start();
+ } else {
+ _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
+ owner->scheduleNavCallBack(kExtraCompletedFlag);
+ }
+ break;
+ case kRobotComingThrough:
+ g_system->delayMillis(2 * 1000);
+ ((PegasusEngine *)g_engine)->die(kDeathRobotThroughNoradDoor);
+ break;
+ case kRobotDying:
+ _robotState = kRobotDead;
+ _levelsMovie.stop();
+ _levelsMovie.setSegment((kNormalSubRoomPressure + kPressureBase) * _levelsScale,
+ (GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
+ _pressureCallBack.setCallBackFlag(kPressureDroppingFlag);
+ _pressureCallBack.scheduleCallBack(kTriggerAtStart, 0, 0);
+ _typeMovie.stop();
+ _typeMovie.setSegment(0, _typeMovie.getDuration());
+ _typeMovie.setTime(kDecreasingPressureTime * _typeScale);
+ _typeMovie.redrawMovieWorld();
+ _typeMovie.show();
+ _downButton.show();
+ _downButton.setCurrentFrameIndex(1);
+ _gameState = kGameOver;
+ allowInput(false);
+ _levelsMovie.setRate(Common::Rational(0x5555, 0x10000) - 1); // Should match door tracker.
+ break;
+ case kRobotDead:
+ allowInput(true);
+ ((NoradDelta *)owner)->playerBeatRobotWithDoor();
+ owner->requestDeleteCurrentInteraction();
+ break;
+ }
+ }
+
+ if ((flags & (kDelayCompletedFlag | kSpotSoundCompletedFlag)) != 0) {
+ switch (_gameState) {
+ case kPlayingPressureMessage:
+ _typeMovie.setTime(kEqualizeTime * _typeScale);
+ _typeMovie.redrawMovieWorld();
+ owner->requestDelay(1, 5, kFilterNoInput, 0);
+ owner->requestSpotSound(_equalizeSoundIn, _equalizeSoundOut, kFilterNoInput, 0);
+ owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingEqualizeMessage;
+ break;
+ case kPlayingEqualizeMessage:
+ _gameState = kWaitingForPlayer;
+ stopChangingPressure();
+ break;
+ case kPlayingDoneMessage:
+ _gameState = kWaitingForPlayer;
+ _typeMovie.stop();
+ _typeMovie.setFlags(0);
+ _typeMovie.hide();
+ if (!_playingAgainstRobot)
+ ((Norad *)_owner)->doneWithPressureDoor();
+ break;
+ }
+ }
+ } else if (notification == &_pressureNotification) {
+ switch (flags) {
+ case kSplashFinished:
+ _levelsMovie.stop();
+ _levelsMovie.setSegment(0, _levelsMovie.getDuration());
+ _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
+ _levelsMovie.redrawMovieWorld();
+
+ if (GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) {
+ _typeMovie.show();
+ owner->requestDelay(1, 5, kFilterNoInput, 0);
+ owner->requestSpotSound(_pressureSoundIn, _pressureSoundOut, kFilterNoInput, 0);
+ owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingPressureMessage;
+ } else {
+ _gameState = kWaitingForPlayer;
+ }
+ break;
+ case kPressureDroppingFlag:
+ _levelsMovie.stop();
+ _levelsMovie.hide();
+ _typeMovie.stop();
+ _typeMovie.hide();
+ _upButton.hide();
+ _downButton.hide();
+ owner->startExtraSequence(kN59PlayerWins2, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ } else if (notification == &_utilityNotification) {
+ switch (flags) {
+ case kDoorJumpsUpFlag:
+ _utilityCallBack.setCallBackFlag(kDoorJumpsBackFlag);
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime + kNavTimePerFrame, kNavTimeScale);
+ _levelsMovie.hide();
+ _typePunched = _typeMovie.isVisible();
+ if (_typePunched == true)
+ _typeMovie.hide();
+ _upButton.hide();
+ _downButton.hide();
+ break;
+ case kDoorJumpsBackFlag:
+ _levelsMovie.show();
+ _upButton.show();
+ _downButton.show();
+ if (_typePunched)
+ _typeMovie.show();
+ break;
+ case kDoorCrushedFlag:
+ _levelsMovie.hide();
+ _typeMovie.hide();
+ _upButton.hide();
+ _downButton.hide();
+ break;
+ }
+ }
+}
+
+void PressureDoor::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ switch (_gameState) {
+ case kWaitingForPlayer:
+ g_allHotspots.activateOneHotspot(_upHotspotID);
+ g_allHotspots.activateOneHotspot(_downHotspotID);
+ if (!_playingAgainstRobot)
+ g_allHotspots.activateOneHotspot(_outHotspotID);
+ break;
+ default:
+ break;
+ }
+}
+
+void PressureDoor::clickInHotspot(const Input &input, const Hotspot *spot) {
+ HotSpotID id = spot->getObjectID();
+
+ if (id == _upHotspotID || id == _downHotspotID) {
+ if (id == _upHotspotID)
+ _doorTracker.setTrackParameters(spot, &_upButton);
+ else
+ _doorTracker.setTrackParameters(spot, &_downButton);
+
+ _doorTracker.startTracking(input);
+ } else {
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+void PressureDoor::incrementPressure(const HotSpotID id) {
+ _typeMovie.stop();
+ _typeMovie.setSegment(0, _typeMovie.getDuration());
+ _typeMovie.setFlags(0);
+
+ if (id == _upHotspotID) {
+ if (GameState.getNoradSubRoomPressure() < kMaxPressure) {
+ GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() + 1);
+ _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
+ _levelsMovie.redrawMovieWorld();
+ _typeMovie.setTime(kIncreasingPressureTime * _typeScale);
+ _typeMovie.redrawMovieWorld();
+ _typeMovie.show();
+ g_AIArea->checkMiddleArea();
+ } else {
+ _typeMovie.hide();
+ }
+ } else if (id == _downHotspotID) {
+ if (GameState.getNoradSubRoomPressure() > kMinPressure) {
+ GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() - 1);
+ _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
+ _levelsMovie.redrawMovieWorld();
+ _typeMovie.setTime(kDecreasingPressureTime * _typeScale);
+ _typeMovie.redrawMovieWorld();
+ _typeMovie.show();
+ g_AIArea->checkMiddleArea();
+ } else {
+ _typeMovie.hide();
+ }
+ }
+}
+
+void PressureDoor::stopChangingPressure() {
+ Neighborhood *owner;
+
+ switch (GameState.getNoradSubRoomPressure()) {
+ case 11:
+ _typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ _typeMovie.start();
+ break;
+ case 10:
+ _typeMovie.setSegment(kCautionLoopStart * _typeScale, kCautionLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ _typeMovie.start();
+ break;
+ case kNormalSubRoomPressure:
+ owner = getOwner();
+ _typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ _gameState = kPlayingDoneMessage;
+ owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
+ _typeMovie.start();
+ break;
+ default:
+ _typeMovie.hide();
+ break;
+ }
+}
+
+bool PressureDoor::canSolve() {
+ if (_playingAgainstRobot)
+ return GameState.getNoradSubRoomPressure() < 11;
+
+ return GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure;
+}
+
+void PressureDoor::doSolve() {
+ if (_playingAgainstRobot) {
+ GameState.setNoradSubRoomPressure(11);
+ _levelsMovie.setTime((11 + kPressureBase) * _levelsScale);
+ _levelsMovie.redrawMovieWorld();
+ _typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ _typeMovie.start();
+ g_AIArea->checkMiddleArea();
+ } else {
+ GameState.setNoradSubRoomPressure(kNormalSubRoomPressure);
+ _levelsMovie.setTime((kNormalSubRoomPressure + kPressureBase) * _levelsScale);
+ _levelsMovie.redrawMovieWorld();
+ _typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ Neighborhood *owner = getOwner();
+ owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingDoneMessage;
+ _typeMovie.start();
+ g_AIArea->checkMiddleArea();
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.h b/engines/pegasus/neighborhood/norad/pressuredoor.h
new file mode 100644
index 0000000000..b2018bfcf7
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/pressuredoor.h
@@ -0,0 +1,93 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+#include "pegasus/neighborhood/norad/pressuretracker.h"
+
+namespace Pegasus {
+
+static const short kNormalSubRoomPressure = 2;
+
+class PressureDoor : public GameInteraction, public NotificationReceiver {
+public:
+ PressureDoor(Neighborhood *, bool isUpperDoor, const HotSpotID, const HotSpotID,
+ const HotSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut,
+ TimeValue equalizeSoundIn, TimeValue equalizeSoundOut);
+ virtual ~PressureDoor() {}
+
+ void incrementPressure(const HotSpotID);
+ void stopChangingPressure();
+
+ void playAgainstRobot();
+
+ bool canSolve();
+ void doSolve();
+
+protected:
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+
+ Movie _levelsMovie;
+ TimeScale _levelsScale;
+ Movie _typeMovie;
+ TimeScale _typeScale;
+ Sprite _upButton;
+ Sprite _downButton;
+ Notification _pressureNotification;
+ NotificationCallBack _pressureCallBack;
+ Notification *_neighborhoodNotification;
+ int _gameState;
+ HotSpotID _upHotspotID;
+ HotSpotID _downHotspotID;
+ HotSpotID _outHotspotID;
+ PressureTracker _doorTracker;
+ TimeValue _pressureSoundIn;
+ TimeValue _pressureSoundOut;
+ TimeValue _equalizeSoundIn;
+ TimeValue _equalizeSoundOut;
+ bool _isUpperDoor;
+
+ bool _playingAgainstRobot, _typePunched;
+ int _robotState, _punchCount;
+ TimeBase _utilityTimer;
+ Notification _utilityNotification;
+ NotificationCallBack _utilityCallBack;
+ TimeValue _punchInTime;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.cpp b/engines/pegasus/neighborhood/norad/pressuretracker.cpp
new file mode 100644
index 0000000000..5aac19dcbe
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/pressuretracker.cpp
@@ -0,0 +1,87 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/hotspot.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/norad/pressuredoor.h"
+#include "pegasus/neighborhood/norad/pressuretracker.h"
+
+namespace Pegasus {
+
+PressureTracker::PressureTracker(PressureDoor *pressureDoor) {
+ _pressureDoor = pressureDoor;
+ _trackSpot = 0;
+ _trackTime = 0;
+}
+
+void PressureTracker::setTrackParameters(const Hotspot *trackSpot, Sprite *trackButton) {
+ _trackSpot = trackSpot;
+ _trackButton = trackButton;
+ _trackTime = 0;
+}
+
+void PressureTracker::activateHotspots() {
+ Tracker::activateHotspots();
+
+ if (_trackSpot)
+ g_allHotspots.activateOneHotspot(_trackSpot->getObjectID());
+}
+
+// For click-hold dragging.
+bool PressureTracker::stopTrackingInput(const Input &input) {
+ return !JMPPPInput::isPressingInput(input);
+}
+
+void PressureTracker::continueTracking(const Input &input) {
+ Common::Point where;
+ input.getInputLocation(where);
+
+ if (g_allHotspots.findHotspot(where) == _trackSpot) {
+ trackPressure();
+ _trackButton->setCurrentFrameIndex(1);
+ } else {
+ _trackButton->setCurrentFrameIndex(0);
+ }
+}
+
+void PressureTracker::startTracking(const Input &input) {
+ Tracker::startTracking(input);
+ trackPressure();
+}
+
+void PressureTracker::stopTracking(const Input &input) {
+ _trackButton->setCurrentFrameIndex(0);
+ _pressureDoor->stopChangingPressure();
+ Tracker::stopTracking(input);
+}
+
+void PressureTracker::trackPressure() {
+ if (g_system->getMillis() - _trackTime > kPressureDoorTrackInterval * 1000 / 60) {
+ _pressureDoor->incrementPressure(_trackSpot->getObjectID());
+ _trackTime = g_system->getMillis();
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.h b/engines/pegasus/neighborhood/norad/pressuretracker.h
new file mode 100644
index 0000000000..6ca7252e22
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/pressuretracker.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H
+
+#include "pegasus/input.h"
+
+namespace Pegasus {
+
+// This class assumes that the globe movie is built at 15 frames per second with a
+// time scale of 600, yielding 40 time unit per frame.
+
+enum PressureTrackDirection {
+ kTrackPressureUp,
+ kTrackPressureDown
+};
+
+static const int kPressureDoorTrackInterval = 45;
+
+class PressureDoor;
+class Sprite;
+
+class PressureTracker : public Tracker {
+public:
+ PressureTracker(PressureDoor *);
+ virtual ~PressureTracker() {}
+
+ void setTrackParameters(const Hotspot *, Sprite *);
+ void continueTracking(const Input &);
+ void startTracking(const Input &);
+ void stopTracking(const Input &);
+ void activateHotspots();
+ bool stopTrackingInput(const Input &);
+
+protected:
+ void trackPressure();
+
+ PressureDoor *_pressureDoor;
+ const Hotspot *_trackSpot;
+ Sprite *_trackButton;
+ long _trackTime;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp
new file mode 100644
index 0000000000..d48481e925
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp
@@ -0,0 +1,1178 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/subcontrolroom.h"
+#include "pegasus/neighborhood/norad/delta/noraddelta.h"
+
+namespace Pegasus {
+
+// Right Monitor times
+
+static const TimeValue kAlphaClawSplashStart = 0;
+static const TimeValue kAlphaClawSplashStop = 4000;
+
+static const TimeValue kDeltaClawSplashStart = 4000;
+static const TimeValue kDeltaClawSplashStop = 8000;
+
+static const TimeValue kClawAtATime = 8000;
+static const TimeValue kClawAtAPinchedTime = 8600;
+static const TimeValue kClawAtATurnedTime = 9200;
+static const TimeValue kClawAtAWithRobotPinchedTime = 9800;
+
+static const TimeValue kClawAtBTime = 10400;
+static const TimeValue kClawAtBPinchedTime = 11000;
+static const TimeValue kClawAtBTurnedTime = 11600;
+static const TimeValue kClawAtBWithRobotTime = 12200;
+static const TimeValue kClawAtBWithRobotPinchedTime = 12800;
+
+static const TimeValue kClawAtCTime = 13400;
+static const TimeValue kClawAtCPinchedTime = 14000;
+static const TimeValue kClawAtCTurnedTime = 14600;
+
+static const TimeValue kClawAtDTime = 15200;
+static const TimeValue kClawAtDPinchedTime = 15800;
+static const TimeValue kClawAtDTurnedTime = 16400;
+
+static const TimeValue kAToBStart = 17000;
+static const TimeValue kAToBStop = 18680;
+static const TimeValue kAPinchStart = 18680;
+static const TimeValue kAPinchStop = 20200;
+static const TimeValue kACCWStart = 20200;
+static const TimeValue kACCWStop = 21600;
+static const TimeValue kACWStart = 21600;
+static const TimeValue kACWStop = 23000;
+
+static const TimeValue kBToAStart = 23000;
+static const TimeValue kBToAStop = 24680;
+static const TimeValue kBToCStart = 24680;
+static const TimeValue kBToCStop = 26520;
+static const TimeValue kBToDStart = 26520;
+static const TimeValue kBToDStop = 28320;
+static const TimeValue kBPinchStart = 28320;
+static const TimeValue kBPinchStop = 29680;
+static const TimeValue kBCCWStart = 29680;
+static const TimeValue kBCCWStop = 31200;
+static const TimeValue kBCWStart = 31200;
+static const TimeValue kBCWStop = 32720;
+
+static const TimeValue kCToBStart = 32720;
+static const TimeValue kCToBStop = 34560;
+static const TimeValue kCPinchStart = 34560;
+static const TimeValue kCPinchStop = 36400;
+static const TimeValue kCCCWStart = 36400;
+static const TimeValue kCCCWStop = 37840;
+static const TimeValue kCCWStart = 37840;
+static const TimeValue kCCWStop = 39280;
+
+static const TimeValue kDToBStart = 39280;
+static const TimeValue kDToBStop = 41080;
+static const TimeValue kDPinchStart = 41080;
+static const TimeValue kDPinchStop = 42600;
+static const TimeValue kDCCWStart = 42600;
+static const TimeValue kDCCWStop = 44000;
+static const TimeValue kDCWStart = 44000;
+static const TimeValue kDCWStop = 45400;
+
+static const TimeValue kRobotApproachStart = 45400;
+static const TimeValue kRobotApproachStop = 56800;
+
+static const TimeValue kCToBWithRobotStart = 56800;
+static const TimeValue kCToBWithRobotStop = 58600;
+
+static const TimeValue kBPinchWithRobotStart = 58600;
+static const TimeValue kBPinchWithRobotStop = 60400;
+static const TimeValue kBToAWithRobotStart = 60400;
+static const TimeValue kBToAWithRobotStop = 62240;
+
+// As usual, times here are in seconds.
+
+// Left monitor times.
+
+static const TimeValue kAlphaSplashStart = 0;
+static const TimeValue kAlphaSplashStop = 2;
+
+static const TimeValue kMainMenuTime = 2;
+static const TimeValue kLaunchPrepRolloverTime = 3;
+static const TimeValue kLaunchPrepHighlightStart = 4;
+static const TimeValue kLaunchPrepHighlightStop = 5;
+static const TimeValue kClawControlRolloverTime = 5;
+static const TimeValue kClawControlHighlightStart = 6;
+static const TimeValue kClawControlHighlightStop = 7;
+
+static const TimeValue kAlphaLaunchPrepStart = 7;
+static const TimeValue kAlphaLaunchPrepStop = 17;
+
+static const TimeValue kClawMenuStart = 17;
+static const TimeValue kClawMenuStop = 18;
+
+static const TimeValue kClawMenuTime = 18;
+
+static const TimeValue kDeltaSplashStart = 19;
+static const TimeValue kDeltaSplashStop = 21;
+
+static const TimeValue kDeltaLaunchPrepStart = 21;
+static const TimeValue kDeltaLaunchPrepStop = 30;
+
+// Right monitor times.
+
+static const NotificationFlags kAlphaSplashFinished = 1;
+static const NotificationFlags kAlphaPrepFinished = kAlphaSplashFinished << 1;
+static const NotificationFlags kPrepHighlightFinished = kAlphaPrepFinished << 1;
+static const NotificationFlags kClawHighlightFinished = kPrepHighlightFinished << 1;
+static const NotificationFlags kClawMenuFinished = kClawHighlightFinished << 1;
+static const NotificationFlags kDeltaSplashFinished = kClawMenuFinished << 1;
+static const NotificationFlags kDeltaPrepFinished = kDeltaSplashFinished << 1;
+
+static const NotificationFlags kSubControlNotificationFlags = kAlphaSplashFinished |
+ kAlphaPrepFinished |
+ kPrepHighlightFinished |
+ kClawHighlightFinished |
+ kClawMenuFinished |
+ kDeltaSplashFinished |
+ kDeltaPrepFinished;
+
+static const NotificationFlags kOneSecondOfMoveFinished = 1;
+
+static const NotificationFlags kGreenBallNotificationFlags = kOneSecondOfMoveFinished;
+
+enum {
+ kButtonDimFrame,
+ kButtonActiveFrame,
+ kButtonHighlightedFrame
+};
+
+enum {
+ kAlphaSplash,
+ kAlphaMainMenu,
+ kDeltaSplash,
+ kDeltaMainMenu,
+ kClawMenu,
+ kPlayingHighlight,
+ kPuttingClawAway
+};
+
+// The owning neighborhood must provide an array of longs which hold the various
+// extra IDs for moving the claw around. In addition, the owner must tell the sub
+// control room interaction what position the claw starts out in (which is also the
+// position the claw must be in before leaving).
+
+// Standard array indices:
+enum {
+ kClawFromAToBIndex,
+ kClawALoopIndex,
+ kClawAPinchIndex,
+ kClawACounterclockwiseIndex,
+ kClawAClockwiseIndex,
+ kClawFromBToAIndex,
+ kClawFromBToCIndex,
+ kClawFromBToDIndex,
+ kClawBLoopIndex,
+ kClawBPinchIndex,
+ kClawBCounterclockwiseIndex,
+ kClawBClockwiseIndex,
+ kClawFromCToBIndex,
+ kClawCLoopIndex,
+ kClawCPinchIndex,
+ kClawCCounterclockwiseIndex,
+ kClawCClockwiseIndex,
+ kClawFromDToBIndex,
+ kClawDLoopIndex,
+ kClawDPinchIndex,
+ kClawDCounterclockwiseIndex,
+ kClawDClockwiseIndex
+};
+
+// Action indices for s_clawStateTable:
+// Can also be used as indices into _buttons (except for kNoActionIndex and kLoopActionIndex).
+enum {
+ kNoActionIndex = -1,
+ kPinchActionIndex = 0,
+ kMoveDownActionIndex,
+ kMoveRightActionIndex,
+ kMoveLeftActionIndex,
+ kMoveUpActionIndex,
+ kCCWActionIndex,
+ kCWActionIndex,
+ kLoopActionIndex
+};
+
+/*
+ _currentAction and _nextAction:
+
+ At any time, _currentAction contains an action index (defined above). The current
+ action index is what the claw is doing right now. If the player presses a button
+ before the current action completes, _nextAction saves the new action and input
+ is disabled. This has the effect of implementing a queue of commands for the claw
+ that can save at most one extra command.
+
+ The general strategy for using _currentAction and _nextAction are:
+ -- If the player presses a claw button and _currentAction is kNoActionIndex,
+ do the command immediately and set _currentAction accordingly.
+ -- If the player presses a claw button and _currentAction is not kNoActionIndex,
+ save the appropriate action index in _nextAction.
+ -- When a command animation completes, set _nextAction to kNoActionIndex, then
+ check if _nextAction has a command waiting in it. If so, play the appriate
+ animation, copy _nextAction into _currentAction and set _nextAction to
+ kNoActionIndex.
+ -- If the player tries to get up, disable input (including all claw buttons) until
+ the player rises. Then, if the claw is in its original position, play the
+ animation of the player rising.
+ -- If the claw needs to be put back, play the first move required to put the
+ claw back by setting _currentAction and playing the appropriate animation.
+ Leave _nextAction alone. When the animation, completes, check to see if the
+ claw is in its original position or not. If so, complete the player rising
+ sequence by playing the rising animation. If not, repeat this whole step.
+
+ Using this general strategy allows the use of a single function,
+ DispatchClawAction, which can both cause the claw to perform a command and saving
+ the next command in _nextAction.
+*/
+
+// Array indexed by [claw position] [action]
+// array yields an index into the neighborhood's extra id table for claw animation or -1.
+static const int s_clawStateTable[4][8] = {
+ {
+ kClawAPinchIndex,
+ kNoActionIndex,
+ kNoActionIndex,
+ kClawFromAToBIndex,
+ kNoActionIndex,
+ kClawACounterclockwiseIndex,
+ kClawAClockwiseIndex,
+ kClawALoopIndex
+ },
+ {
+ kClawBPinchIndex,
+ kNoActionIndex,
+ kClawFromBToAIndex,
+ kClawFromBToDIndex,
+ kClawFromBToCIndex,
+ kClawBCounterclockwiseIndex,
+ kClawBClockwiseIndex,
+ kClawBLoopIndex
+ },
+ {
+ kClawCPinchIndex,
+ kClawFromCToBIndex,
+ kNoActionIndex,
+ kNoActionIndex,
+ kNoActionIndex,
+ kClawCCounterclockwiseIndex,
+ kClawCClockwiseIndex,
+ kClawCLoopIndex
+ },
+ {
+ kClawDPinchIndex,
+ kNoActionIndex,
+ kClawFromDToBIndex,
+ kNoActionIndex,
+ kNoActionIndex,
+ kClawDCounterclockwiseIndex,
+ kClawDClockwiseIndex,
+ kClawDLoopIndex
+ }
+};
+
+// Move directions for s_clawMovieTable:
+enum {
+ kMoveClawDown,
+ kMoveClawRight,
+ kMoveClawLeft,
+ kMoveClawUp
+};
+
+static const int kClawNowhere = -1;
+
+// Array indexed by [claw position] [move direction]
+// array yields new claw position or -1.
+static const int s_clawMovieTable[4][4] = {
+ {
+ kClawNowhere,
+ kClawNowhere,
+ kClawAtB,
+ kClawNowhere
+ },
+ {
+ kClawNowhere,
+ kClawAtA,
+ kClawAtD,
+ kClawAtC
+ },
+ {
+ kClawAtB,
+ kClawNowhere,
+ kClawNowhere,
+ kClawNowhere
+ },
+ {
+ kClawNowhere,
+ kClawAtB,
+ kClawNowhere,
+ kClawNowhere
+ }
+};
+
+// Indexed by claw action index, claw position, plus 0 for start, 1 for stop.
+// (Never indexed with kLoopActionIndex.)
+static const TimeValue s_clawMonitorTable[7][4][2] = {
+ {
+ { kAPinchStart, kAPinchStop },
+ { kBPinchStart, kBPinchStop },
+ { kCPinchStart, kCPinchStop },
+ { kDPinchStart, kDPinchStop }
+ },
+ {
+ { 0xffffffff, 0xffffffff },
+ { 0xffffffff, 0xffffffff },
+ { kCToBStart, kCToBStop },
+ { 0xffffffff, 0xffffffff }
+ },
+ {
+ { 0xffffffff, 0xffffffff },
+ { kBToAStart, kBToAStop },
+ { 0xffffffff, 0xffffffff },
+ { kDToBStart, kDToBStop }
+ },
+ {
+ { kAToBStart, kAToBStop },
+ { kBToDStart, kBToDStop },
+ { 0xffffffff, 0xffffffff },
+ { 0xffffffff, 0xffffffff }
+ },
+ {
+ { 0xffffffff, 0xffffffff },
+ { kBToCStart, kBToCStop },
+ { 0xffffffff, 0xffffffff },
+ { 0xffffffff, 0xffffffff }
+ },
+ {
+ { kACCWStart, kACCWStop },
+ { kBCCWStart, kBCCWStop },
+ { kCCCWStart, kCCCWStop },
+ { kDCCWStart, kDCCWStop }
+ },
+ {
+ { kACWStart, kACWStop },
+ { kBCWStart, kBCWStop },
+ { kCCWStart, kCCWStop },
+ { kDCWStart, kDCWStop }
+ }
+};
+
+// Frame indices for the green ball sprite.
+enum {
+ kGreenBallAtA,
+ kGreenBallAtAWithClaw,
+ kGreenBallAtAWithClawAndRobot,
+ kGreenBallAtB,
+ kGreenBallAtBWithClaw,
+ kGreenBallAtBWithClawAndRobot,
+ kGreenBallAtCArmAtA,
+ kGreenBallAtCArmAtB,
+ kGreenBallAtCArmAtD,
+ kGreenBallAtCWithClaw,
+ kGreenBallAtD,
+ kGreenBallAtDWithClaw,
+ kNumClawGreenBalls
+};
+
+// State constants for _robotState.
+enum {
+ kNoRobot,
+ kRobotApproaching,
+ kPunchingOnce,
+ kPunchingTwice,
+ kPunchingThrice,
+ kCarriedToDoor,
+ kPlayerWon,
+ kRobotWon
+};
+
+// Sub Control Room button PICTs:
+static const ResIDType kSubControlButtonBaseID = 500;
+static const ResIDType kClawMonitorGreenBallBaseID = 600;
+
+// Constructor
+SubControlRoom::SubControlRoom(Neighborhood *handler) : GameInteraction(kNoradSubControlRoomInteractionID, handler),
+ _subControlMovie(kSubControlMonitorID), _subControlNotification(kSubControlNotificationID, (PegasusEngine *)g_engine),
+ _clawMonitorMovie(kClawMonitorID), _pinchButton(kSubControlPinchID), _downButton(kSubControlDownID),
+ _rightButton(kSubControlRightID), _leftButton(kSubControlLeftID), _upButton(kSubControlUpID),
+ _ccwButton(kSubControlCCWID), _cwButton(kSubControlCWID), _greenBall(kClawMonitorGreenBallID),
+ _greenBallNotification(kNoradGreenBallNotificationID, (PegasusEngine *)g_engine) {
+ _neighborhoodNotification = handler->getNeighborhoodNotification();
+ _playingAgainstRobot = false;
+ _robotState = kNoRobot;
+}
+
+void SubControlRoom::playAgainstRobot() {
+ _playingAgainstRobot = true;
+}
+
+void SubControlRoom::openInteraction() {
+ _currentAction = kNoActionIndex;
+ _nextAction = kNoActionIndex;
+
+ Norad *owner = (Norad *)getOwner();
+ owner->getClawInfo(_outSpotID, _prepSpotID, _clawControlSpotID, _clawButtonSpotIDs[0],
+ _clawButtonSpotIDs[1], _clawButtonSpotIDs[2], _clawButtonSpotIDs[3],
+ _clawButtonSpotIDs[4], _clawButtonSpotIDs[5], _clawButtonSpotIDs[6],
+ _clawStartPosition, _clawExtraIDs);
+
+ _clawPosition = _clawStartPosition;
+ _clawNextPosition = _clawPosition;
+ _subControlMovie.initFromMovieFile("Images/Norad Alpha/N22 Left Monitor Movie");
+ _subControlMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+ _subControlMovie.moveElementTo(kNoradSubControlLeft, kNoradSubControlTop);
+ _subControlScale = _subControlMovie.getScale();
+ _subControlMovie.setDisplayOrder(kSubControlOrder);
+ _subControlMovie.startDisplaying();
+ _subControlCallBack.setNotification(&_subControlNotification);
+ _subControlCallBack.initCallBack(&_subControlMovie, kCallBackAtExtremes);
+
+ _clawMonitorMovie.initFromMovieFile("Images/Norad Alpha/N22:N60 Right Monitor");
+ _clawMonitorMovie.moveElementTo(kNoradClawMonitorLeft, kNoradClawMonitorTop);
+ _clawMonitorMovie.setDisplayOrder(kClawMonitorOrder);
+ _clawMonitorMovie.startDisplaying();
+ _clawMonitorCallBack.setNotification(&_subControlNotification);
+ _clawMonitorCallBack.initCallBack(&_clawMonitorMovie, kCallBackAtExtremes);
+
+ _subControlNotification.notifyMe(this, kSubControlNotificationFlags, kSubControlNotificationFlags);
+
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
+
+ _buttons[0] = &_pinchButton;
+ _buttons[1] = &_downButton;
+ _buttons[2] = &_rightButton;
+ _buttons[3] = &_leftButton;
+ _buttons[4] = &_upButton;
+ _buttons[5] = &_ccwButton;
+ _buttons[6] = &_cwButton;
+
+ _pinchButton.setDisplayOrder(kSubControlPinchOrder);
+ _downButton.setDisplayOrder(kSubControlDownOrder);
+ _rightButton.setDisplayOrder(kSubControlRightOrder);
+ _leftButton.setDisplayOrder(kSubControlLeftOrder);
+ _upButton.setDisplayOrder(kSubControlUpOrder);
+ _ccwButton.setDisplayOrder(kSubControlCCWOrder);
+ _cwButton.setDisplayOrder(kSubControlCWOrder);
+
+ for (int i = 0; i < kNumClawButtons; i++) {
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3, true);
+ _buttons[i]->addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 1, true);
+ _buttons[i]->addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 2, true);
+ _buttons[i]->addFrame(frame, 0, 0);
+
+ _buttons[i]->setCurrentFrameIndex(0);
+ _buttons[i]->startDisplaying();
+ }
+
+ _pinchButton.moveElementTo(kNoradSubControlPinchLeft, kNoradSubControlPinchTop);
+ _downButton.moveElementTo(kNoradSubControlDownLeft, kNoradSubControlDownTop);
+ _rightButton.moveElementTo(kNoradSubControlRightLeft, kNoradSubControlRightTop);
+ _leftButton.moveElementTo(kNoradSubControlLeftLeft, kNoradSubControlLeftTop);
+ _upButton.moveElementTo(kNoradSubControlUpLeft, kNoradSubControlUpTop);
+ _ccwButton.moveElementTo(kNoradSubControlCCWLeft, kNoradSubControlCCWTop);
+ _cwButton.moveElementTo(kNoradSubControlCWLeft, kNoradSubControlCWTop);
+
+ _greenBall.setDisplayOrder(kClawMonitorGreenBallOrder);
+
+ for (int i = 0; i < kNumClawGreenBalls; i++) {
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kClawMonitorGreenBallBaseID + i);
+ _greenBall.addFrame(frame, 0, 0);
+ }
+
+ _greenBall.setCurrentFrameIndex(0);
+ _greenBall.startDisplaying();
+
+ _greenBallTimer.setScale(owner->getNavMovie()->getScale());
+ _greenBallCallBack.setNotification(&_greenBallNotification);
+ _greenBallCallBack.initCallBack(&_greenBallTimer, kCallBackAtExtremes);
+ _greenBallCallBack.setCallBackFlag(kOneSecondOfMoveFinished);
+ _greenBallNotification.notifyMe(this, kGreenBallNotificationFlags, kGreenBallNotificationFlags);
+
+ _subControlMovie.show();
+ _clawMonitorMovie.show();
+}
+
+void SubControlRoom::initInteraction() {
+ if (GameState.getNoradSubPrepState() == kSubDamaged) {
+ playControlMonitorSection(kDeltaSplashStart * _subControlScale, kDeltaSplashStop * _subControlScale,
+ 0, kDeltaSplash, false);
+ playClawMonitorSection(kDeltaClawSplashStart, kDeltaClawSplashStop, kDeltaSplashFinished, _gameState, false);
+ } else {
+ playControlMonitorSection(kAlphaSplashStart * _subControlScale, kAlphaSplashStop * _subControlScale,
+ 0, kAlphaSplash, false);
+ playClawMonitorSection(kAlphaClawSplashStart, kAlphaClawSplashStop, kAlphaSplashFinished, _gameState, false);
+ }
+
+ _subControlMovie.redrawMovieWorld();
+ _clawMonitorMovie.redrawMovieWorld();
+}
+
+void SubControlRoom::closeInteraction() {
+ _subControlNotification.cancelNotification(this);
+ _subControlCallBack.releaseCallBack();
+ _greenBallNotification.cancelNotification(this);
+ _greenBallCallBack.releaseCallBack();
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void SubControlRoom::setSoundFXLevel(const uint16 fxLevel) {
+ _subControlMovie.setVolume(fxLevel);
+}
+
+void SubControlRoom::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ Norad *owner = (Norad *)getOwner();
+
+ if (notification == &_subControlNotification) {
+ switch (flags) {
+ case kAlphaSplashFinished:
+ setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true);
+ break;
+ case kPrepHighlightFinished:
+ if (GameState.getNoradSubPrepState() == kSubDamaged)
+ playControlMonitorSection(kDeltaLaunchPrepStart * _subControlScale,
+ kDeltaLaunchPrepStop * _subControlScale, kDeltaPrepFinished, _gameState, false);
+ else
+ playControlMonitorSection(kAlphaLaunchPrepStart * _subControlScale,
+ kAlphaLaunchPrepStop * _subControlScale, kAlphaPrepFinished, _gameState, false);
+ break;
+ case kAlphaPrepFinished:
+ GameState.setNoradSubPrepState(kSubPrepped);
+ GameState.setScoringPreppedSub(true);
+ setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true);
+ break;
+ case kClawHighlightFinished:
+ playControlMonitorSection(kClawMenuStart * _subControlScale, kClawMenuStop * _subControlScale,
+ kClawMenuFinished, _gameState, false);
+ break;
+ case kClawMenuFinished:
+ owner->playClawMonitorIntro();
+ showButtons();
+ setControlMonitorToTime(kClawMenuTime * _subControlScale, kClawMenu, true);
+
+ if (!_playingAgainstRobot) {
+ updateClawMonitor();
+ owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]);
+ }
+ break;
+ case kDeltaSplashFinished:
+ setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true);
+
+ if (_playingAgainstRobot) {
+ _robotState = kRobotApproaching;
+ playClawMonitorSection(kRobotApproachStart, kRobotApproachStop, 0, _gameState, true);
+ owner->startExtraSequence(kN60RobotApproaches, kExtraCompletedFlag, kFilterAllInput);
+ }
+ break;
+ case kDeltaPrepFinished:
+ setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true);
+ break;
+ }
+ } else if (notification == &_greenBallNotification) {
+ if (_robotState == kRobotWon) {
+ // We are using the green ball notification to hide stuff when the robot comes through
+ // the glass.
+ hideEverything();
+ } else {
+ // We are now midway through a move, time to update the claw's position and the green
+ // ball.
+ _clawPosition = _clawNextPosition;
+ updateClawMonitor();
+ updateGreenBall();
+ }
+ } else if (notification == _neighborhoodNotification) {
+ _currentAction = kNoActionIndex;
+ if (_playingAgainstRobot) {
+ switch (_robotState) {
+ case kRobotApproaching:
+ if (_gameState == kClawMenu) {
+ _robotState = kPunchingOnce;
+ dispatchClawAction(kNoActionIndex);
+ } else {
+ robotKillsPlayer(kN60FirstMistake, owner);
+ }
+ break;
+ case kPunchingOnce:
+ if (_nextAction == kMoveDownActionIndex) {
+ _robotState = kPunchingTwice;
+ performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner);
+ } else {
+ robotKillsPlayer(kN60SecondMistake, owner);
+ }
+ break;
+ case kPunchingTwice:
+ if (_nextAction == kPinchActionIndex) {
+ _robotState = kPunchingThrice;
+ performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner);
+ } else {
+ robotKillsPlayer(kN60ThirdMistake, owner);
+ }
+ break;
+ case kPunchingThrice:
+ if (_nextAction == kMoveRightActionIndex) {
+ _robotState = kCarriedToDoor;
+ performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner);
+ } else {
+ robotKillsPlayer(kN60FourthMistake, owner);
+ }
+ break;
+ case kCarriedToDoor:
+ hideEverything();
+ _robotState = kPlayerWon;
+ owner->startExtraSequence(kN60PlayerFollowsRobotToDoor, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ case kPlayerWon:
+ ((NoradDelta *)owner)->playerBeatRobotWithClaw();
+ owner->requestDeleteCurrentInteraction();
+ break;
+ case kRobotWon:
+ g_system->delayMillis(2 * 1000); // 120 ticks
+ ((PegasusEngine *)g_engine)->die(kDeathRobotSubControlRoom);
+ break;
+ }
+ } else {
+ if (_gameState == kPuttingClawAway && _nextAction == kNoActionIndex) {
+ if (_clawPosition == _clawStartPosition) {
+ Input scratch;
+ GameInteraction::clickInHotspot(scratch, g_allHotspots.findHotspotByID(_outSpotID));
+ } else {
+ switch (_clawPosition) {
+ case kClawAtA:
+ dispatchClawAction(kMoveLeftActionIndex);
+ break;
+ case kClawAtB:
+ if (_clawStartPosition == kClawAtD) // Norad Alpha
+ dispatchClawAction(kMoveLeftActionIndex);
+ else if (_clawStartPosition == kClawAtC) // Norad Delta
+ dispatchClawAction(kMoveUpActionIndex);
+ break;
+ case kClawAtC:
+ dispatchClawAction(kMoveDownActionIndex);
+ break;
+ case kClawAtD:
+ dispatchClawAction(kMoveRightActionIndex);
+ break;
+ }
+ }
+ } else {
+ dispatchClawAction(_nextAction);
+ }
+ }
+ }
+}
+
+void SubControlRoom::hideEverything() {
+ hideButtons();
+ _subControlMovie.hide();
+ _clawMonitorMovie.hide();
+ _greenBall.hide();
+}
+
+void SubControlRoom::robotKillsPlayer(const uint32 extraID, Neighborhood *owner) {
+ _robotState = kRobotWon;
+ owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput);
+ _greenBallTimer.stop();
+ _greenBallTimer.setSegment(0, 32 * _greenBallTimer.getScale() / 15);
+ _greenBallTimer.setTime(0);
+ _greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _greenBallTimer.start();
+}
+
+void SubControlRoom::activateHotspots() {
+ if (_robotState == kRobotWon || _robotState == kPlayerWon)
+ return;
+
+ GameInteraction::activateHotspots();
+
+ switch (_gameState) {
+ case kAlphaMainMenu:
+ case kDeltaMainMenu:
+ g_allHotspots.activateOneHotspot(_prepSpotID);
+ g_allHotspots.activateOneHotspot(_clawControlSpotID);
+ break;
+ case kClawMenu:
+ // This could be called during a move, so use _clawNextPosition.
+ if (_playingAgainstRobot) {
+ g_allHotspots.deactivateOneHotspot(_outSpotID);
+ if (_robotState != kRobotApproaching && _nextAction == kNoActionIndex)
+ for (int i = 0; i < kNumClawButtons; i++)
+ if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex)
+ g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]);
+ } else if (_nextAction == kNoActionIndex) {
+ for (int i = 0; i < kNumClawButtons; i++)
+ if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex)
+ g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void SubControlRoom::showButtons() {
+ if (_playingAgainstRobot && _robotState == kRobotApproaching) {
+ for (int i = 0; i < kNumClawButtons; i++) {
+ _buttons[i]->show();
+ _buttons[i]->setCurrentFrameIndex(kButtonDimFrame);
+ }
+ } else if (_nextAction != kNoActionIndex) {
+ for (int i = 0; i < kNumClawButtons; i++) {
+ _buttons[i]->show();
+ if (i == _currentAction || i == _nextAction)
+ _buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame);
+ else
+ _buttons[i]->setCurrentFrameIndex(kButtonDimFrame);
+ }
+ } else {
+ for (int i = 0; i < kNumClawButtons; i++) {
+ _buttons[i]->show();
+ if (i == _currentAction)
+ _buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame);
+ else if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex &&
+ _gameState != kPuttingClawAway) // this could be called during a move, so check _clawNextPosition
+ _buttons[i]->setCurrentFrameIndex(kButtonActiveFrame);
+ else
+ _buttons[i]->setCurrentFrameIndex(kButtonDimFrame);
+ }
+ }
+}
+
+void SubControlRoom::hideButtons() {
+ for (int i = 0; i < kNumClawButtons; i++)
+ _buttons[i]->hide();
+}
+
+int SubControlRoom::findActionIndex(HotSpotID id) {
+ for (int i = 0; i < kNumClawButtons; i++)
+ if (id == _clawButtonSpotIDs[i])
+ return i;
+
+ return kNoActionIndex;
+}
+
+void SubControlRoom::clickInHotspot(const Input &input, const Hotspot *spot) {
+ HotSpotID clickedID = spot->getObjectID();
+ int actionIndex = findActionIndex(clickedID);
+
+ if (actionIndex != kNoActionIndex) {
+ dispatchClawAction(actionIndex);
+ } else if (clickedID == _prepSpotID) {
+ playControlMonitorSection(kLaunchPrepHighlightStart * _subControlScale,
+ kLaunchPrepHighlightStop * _subControlScale,
+ kPrepHighlightFinished, kPlayingHighlight, false);
+ } else if (clickedID == _clawControlSpotID) {
+ playControlMonitorSection(kClawControlHighlightStart * _subControlScale,
+ kClawControlHighlightStop * _subControlScale,
+ kClawHighlightFinished, kPlayingHighlight, false);
+ } else if (clickedID == _outSpotID) {
+ _gameState = kPuttingClawAway;
+
+ if (_currentAction == kNoActionIndex) {
+ if (_clawPosition == _clawStartPosition) {
+ GameInteraction::clickInHotspot(input, spot);
+ } else {
+ switch (_clawPosition) {
+ case kClawAtA:
+ dispatchClawAction(kMoveLeftActionIndex);
+ break;
+ case kClawAtB:
+ if (_clawStartPosition == kClawAtD) // Norad Alpha
+ dispatchClawAction(kMoveLeftActionIndex);
+ else if (_clawStartPosition == kClawAtC) // Norad Delta
+ dispatchClawAction(kMoveUpActionIndex);
+ break;
+ case kClawAtC:
+ dispatchClawAction(kMoveDownActionIndex);
+ break;
+ case kClawAtD:
+ dispatchClawAction(kMoveRightActionIndex);
+ break;
+ }
+ }
+ }
+ } else {
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+void SubControlRoom::dispatchClawAction(const int newAction) {
+ GameState.setScoringPlayedWithClaw(true);
+
+ Neighborhood *owner = getOwner();
+
+ if (newAction == kNoActionIndex) {
+ _currentAction = kNoActionIndex;
+ _nextAction = kNoActionIndex;
+ showButtons();
+ updateGreenBall();
+
+ if (_playingAgainstRobot)
+ owner->startExtraSequence(kN60ArmActivated, kExtraCompletedFlag, kFilterAllInput);
+ else
+ owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]);
+ } else {
+ if (_currentAction == kNoActionIndex) {
+ if (_playingAgainstRobot) {
+ _nextAction = newAction;
+ showButtons();
+ updateGreenBall();
+ } else {
+ performActionImmediately(newAction, _clawExtraIDs[s_clawStateTable[_clawPosition][newAction]], owner);
+ }
+ } else if (_nextAction == kNoActionIndex) {
+ _nextAction = newAction;
+ showButtons();
+ updateGreenBall();
+ }
+ }
+}
+
+void SubControlRoom::performActionImmediately(const int action, const uint32 extraID, Neighborhood *owner) {
+ _currentAction = action;
+ _nextAction = kNoActionIndex;
+ ExtraTable::Entry entry;
+
+ switch (action) {
+ case kMoveDownActionIndex:
+ case kMoveRightActionIndex:
+ case kMoveLeftActionIndex:
+ case kMoveUpActionIndex:
+ // Set up green ball callback.
+ owner->getExtraEntry(extraID, entry);
+ _greenBallTimer.stop();
+ _greenBallTimer.setSegment(entry.movieStart, entry.movieStart + owner->getNavMovie()->getScale());
+ _greenBallTimer.setTime(entry.movieStart);
+ _greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ // Start move.
+ _greenBallTimer.start();
+ break;
+ }
+
+ if (_playingAgainstRobot) {
+ switch (_robotState) {
+ case kPunchingTwice:
+ owner->startExtraSequence(kN60ArmToPositionB, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ case kPunchingThrice:
+ owner->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ case kCarriedToDoor:
+ owner->startExtraSequence(kN60ArmCarriesRobotToPositionA, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ }
+ } else {
+ owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput);
+ }
+
+ switch (action) {
+ case kMoveDownActionIndex:
+ _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawDown];
+ break;
+ case kMoveRightActionIndex:
+ _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawRight];
+ break;
+ case kMoveLeftActionIndex:
+ _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawLeft];
+ break;
+ case kMoveUpActionIndex:
+ _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawUp];
+ break;
+ case kLoopActionIndex:
+ // Do nothing.
+ break;
+ default:
+ playClawMonitorSection(s_clawMonitorTable[action][_clawPosition][0],
+ s_clawMonitorTable[action][_clawPosition][1], 0, _gameState, true);
+ break;
+ }
+
+ showButtons();
+ updateGreenBall();
+}
+
+void SubControlRoom::setControlMonitorToTime(const TimeValue newTime, const int newState, const bool shouldAllowInput) {
+ _subControlMovie.stop();
+ _subControlMovie.setSegment(0, _subControlMovie.getDuration());
+ _subControlMovie.setTime(newTime);
+ _subControlMovie.redrawMovieWorld();
+ _gameState = newState;
+ allowInput(shouldAllowInput);
+}
+
+void SubControlRoom::playControlMonitorSection(const TimeValue in, const TimeValue out, const NotificationFlags flags,
+ const int newState, const bool shouldAllowInput) {
+ _subControlMovie.stop();
+ _subControlMovie.setSegment(in, out);
+ _subControlMovie.setTime(in);
+
+ if (flags != 0) {
+ _subControlCallBack.setCallBackFlag(flags);
+ _subControlCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ _gameState = newState;
+ allowInput(shouldAllowInput);
+ _subControlMovie.start();
+}
+
+void SubControlRoom::updateClawMonitor() {
+ switch (_clawPosition) {
+ case kClawAtA:
+ setClawMonitorToTime(kClawAtATime);
+ break;
+ case kClawAtB:
+ setClawMonitorToTime(kClawAtBTime);
+ break;
+ case kClawAtC:
+ setClawMonitorToTime(kClawAtCTime);
+ break;
+ case kClawAtD:
+ setClawMonitorToTime(kClawAtDTime);
+ break;
+ }
+}
+
+void SubControlRoom::setClawMonitorToTime(const TimeValue newTime) {
+ _clawMonitorMovie.stop();
+ _clawMonitorMovie.setSegment(0, _clawMonitorMovie.getDuration());
+ _clawMonitorMovie.setTime(newTime);
+ _clawMonitorMovie.redrawMovieWorld();
+}
+
+void SubControlRoom::playClawMonitorSection(const TimeValue in, const TimeValue out, const NotificationFlags flags,
+ const int newState, const bool shouldAllowInput) {
+ _clawMonitorMovie.stop();
+ _clawMonitorMovie.setSegment(in, out);
+ _clawMonitorMovie.setTime(in);
+
+ if (flags != 0) {
+ _clawMonitorCallBack.setCallBackFlag(flags);
+ _clawMonitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ _gameState = newState;
+ allowInput(shouldAllowInput);
+ _clawMonitorMovie.start();
+}
+
+void SubControlRoom::updateGreenBall() {
+ switch (_currentAction) {
+ case kMoveDownActionIndex:
+ switch (_nextAction) {
+ case kMoveRightActionIndex:
+ moveGreenBallToA();
+ break;
+ case kMoveLeftActionIndex:
+ moveGreenBallToD();
+ break;
+ case kMoveUpActionIndex:
+ moveGreenBallToC();
+ break;
+ default:
+ moveGreenBallToB();
+ break;
+ }
+ break;
+ case kMoveRightActionIndex:
+ if (_clawNextPosition == kClawAtA) {
+ switch (_nextAction) {
+ case kMoveLeftActionIndex:
+ moveGreenBallToB();
+ break;
+ default:
+ moveGreenBallToA();
+ break;
+ }
+ } else {
+ switch (_nextAction) {
+ case kMoveRightActionIndex:
+ moveGreenBallToA();
+ break;
+ case kMoveLeftActionIndex:
+ moveGreenBallToD();
+ break;
+ case kMoveUpActionIndex:
+ moveGreenBallToC();
+ break;
+ default:
+ moveGreenBallToB();
+ break;
+ }
+ }
+ break;
+ case kMoveLeftActionIndex:
+ if (_clawNextPosition == kClawAtB) {
+ switch (_nextAction) {
+ case kMoveRightActionIndex:
+ moveGreenBallToA();
+ break;
+ case kMoveLeftActionIndex:
+ moveGreenBallToD();
+ break;
+ case kMoveUpActionIndex:
+ moveGreenBallToC();
+ break;
+ default:
+ moveGreenBallToB();
+ break;
+ }
+ } else {
+ switch (_nextAction) {
+ case kMoveRightActionIndex:
+ moveGreenBallToB();
+ break;
+ default:
+ moveGreenBallToD();
+ break;
+ }
+ }
+ break;
+ case kMoveUpActionIndex:
+ switch (_nextAction) {
+ case kMoveDownActionIndex:
+ moveGreenBallToB();
+ break;
+ default:
+ moveGreenBallToC();
+ break;
+ }
+ break;
+ default:
+ switch (_nextAction) {
+ case kMoveDownActionIndex:
+ moveGreenBallToB();
+ break;
+ case kMoveRightActionIndex:
+ if (_clawPosition == kClawAtB)
+ moveGreenBallToA();
+ else
+ moveGreenBallToB();
+ break;
+ case kMoveLeftActionIndex:
+ if (_clawPosition == kClawAtB)
+ moveGreenBallToD();
+ else
+ moveGreenBallToB();
+ break;
+ case kMoveUpActionIndex:
+ moveGreenBallToC();
+ break;
+ default:
+ _greenBall.hide();
+ break;
+ }
+ break;
+ }
+}
+
+void SubControlRoom::moveGreenBallToA() {
+ if (_clawPosition == kClawAtA) {
+ if (_playingAgainstRobot)
+ _greenBall.setCurrentFrameIndex(kGreenBallAtAWithClawAndRobot);
+ else
+ _greenBall.setCurrentFrameIndex(kGreenBallAtAWithClaw);
+ } else {
+ _greenBall.setCurrentFrameIndex(kGreenBallAtA);
+ }
+
+ _greenBall.moveElementTo(kNoradGreenBallAtALeft, kNoradGreenBallAtATop);
+ _greenBall.show();
+}
+
+void SubControlRoom::moveGreenBallToB() {
+ if (_clawPosition == kClawAtB) {
+ if (_playingAgainstRobot)
+ _greenBall.setCurrentFrameIndex(kGreenBallAtBWithClawAndRobot);
+ else
+ _greenBall.setCurrentFrameIndex(kGreenBallAtBWithClaw);
+ } else {
+ _greenBall.setCurrentFrameIndex(kGreenBallAtB);
+ }
+
+ _greenBall.moveElementTo(kNoradGreenBallAtBLeft, kNoradGreenBallAtBTop);
+ _greenBall.show();
+}
+
+void SubControlRoom::moveGreenBallToC() {
+ switch (_clawPosition) {
+ case kClawAtA:
+ _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtA);
+ break;
+ case kClawAtB:
+ _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtB);
+ break;
+ case kClawAtC:
+ _greenBall.setCurrentFrameIndex(kGreenBallAtCWithClaw);
+ break;
+ case kClawAtD:
+ _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtD);
+ break;
+ }
+
+ _greenBall.moveElementTo(kNoradGreenBallAtCLeft, kNoradGreenBallAtCTop);
+ _greenBall.show();
+}
+
+void SubControlRoom::moveGreenBallToD() {
+ if (_clawPosition == kClawAtD)
+ _greenBall.setCurrentFrameIndex(kGreenBallAtDWithClaw);
+ else
+ _greenBall.setCurrentFrameIndex(kGreenBallAtD);
+
+ _greenBall.moveElementTo(kNoradGreenBallAtDLeft, kNoradGreenBallAtDTop);
+ _greenBall.show();
+}
+
+bool SubControlRoom::canSolve() {
+ return _playingAgainstRobot && _robotState < kCarriedToDoor;
+}
+
+void SubControlRoom::doSolve() {
+ _robotState = kCarriedToDoor;
+ hideEverything();
+ getOwner()->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput);
+}
+
+InputBits SubControlRoom::getInputFilter() {
+ if (_playingAgainstRobot)
+ return GameInteraction::getInputFilter() & ~kFilterDownButtonAny;
+
+ return GameInteraction::getInputFilter();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.h b/engines/pegasus/neighborhood/norad/subcontrolroom.h
new file mode 100644
index 0000000000..6ce599db42
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/subcontrolroom.h
@@ -0,0 +1,133 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/notification.h"
+
+namespace Pegasus {
+
+static const uint32 kClawAtA = 0;
+static const uint32 kClawAtB = 1;
+static const uint32 kClawAtC = 2;
+static const uint32 kClawAtD = 3;
+
+static const int kNumClawButtons = 7;
+
+class Norad;
+
+class SubControlRoom : public GameInteraction, public NotificationReceiver {
+public:
+ SubControlRoom(Neighborhood *);
+ virtual ~SubControlRoom() {}
+
+ void playAgainstRobot();
+
+ virtual void setSoundFXLevel(const uint16);
+
+ bool canSolve();
+ void doSolve();
+
+protected:
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+
+ void robotKillsPlayer(const uint32, Neighborhood *);
+ InputBits getInputFilter();
+
+ int findActionIndex(HotSpotID);
+ void dispatchClawAction(const int);
+ void performActionImmediately(const int, const uint32, Neighborhood *);
+
+ void hideEverything();
+ void showButtons();
+ void hideButtons();
+
+ void updateGreenBall();
+ void moveGreenBallToA();
+ void moveGreenBallToB();
+ void moveGreenBallToC();
+ void moveGreenBallToD();
+
+ void setControlMonitorToTime(const TimeValue, const int, const bool);
+ void playControlMonitorSection(const TimeValue, const TimeValue, const NotificationFlags,
+ const int, const bool);
+
+ void updateClawMonitor();
+ void setClawMonitorToTime(const TimeValue);
+ void playClawMonitorSection(const TimeValue, const TimeValue, const NotificationFlags,
+ const int, const bool);
+
+ Movie _subControlMovie;
+ TimeScale _subControlScale;
+ Notification _subControlNotification;
+ NotificationCallBack _subControlCallBack;
+ Movie _clawMonitorMovie;
+ NotificationCallBack _clawMonitorCallBack;
+ int _gameState;
+ uint32 _clawStartPosition;
+ uint32 _clawPosition;
+ uint32 _clawNextPosition;
+ const uint32 *_clawExtraIDs;
+
+ int _currentAction;
+ int _nextAction;
+
+ Sprite *_buttons[kNumClawButtons];
+ Sprite _pinchButton;
+ Sprite _downButton;
+ Sprite _rightButton;
+ Sprite _leftButton;
+ Sprite _upButton;
+ Sprite _ccwButton;
+ Sprite _cwButton;
+
+ Sprite _greenBall;
+ TimeBase _greenBallTimer;
+ Notification _greenBallNotification;
+ NotificationCallBack _greenBallCallBack;
+
+ HotSpotID _outSpotID;
+ HotSpotID _prepSpotID;
+ HotSpotID _clawControlSpotID;
+ HotSpotID _clawButtonSpotIDs[kNumClawButtons];
+
+ Notification *_neighborhoodNotification;
+
+ bool _playingAgainstRobot;
+ int _robotState;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/subplatform.cpp b/engines/pegasus/neighborhood/norad/subplatform.cpp
new file mode 100644
index 0000000000..97079a9f53
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/subplatform.cpp
@@ -0,0 +1,205 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/subplatform.h"
+#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
+
+namespace Pegasus {
+
+// As usual, times here are in seconds.
+
+static const TimeValue kNormalSplashStart = 0;
+static const TimeValue kNormalSplashStop = 5;
+
+static const TimeValue kPrepSubStart = 5;
+static const TimeValue kPrepSubStop = 15;
+
+static const TimeValue kPrepIncompleteStart = 15;
+static const TimeValue kPrepIncompleteStop = 19;
+
+static const TimeValue kDamagedStart = 19;
+static const TimeValue kDamagedStop = 28;
+
+static const NotificationFlags kNormalSplashFinished = 1;
+static const NotificationFlags kPrepSubFinished = kNormalSplashFinished << 1;
+static const NotificationFlags kPrepIncompleteFinished = kPrepSubFinished << 1;
+static const NotificationFlags kDamagedFinished = kPrepIncompleteFinished << 1;
+
+static const NotificationFlags kPlatformNotificationFlags = kNormalSplashFinished |
+ kPrepSubFinished |
+ kPrepIncompleteFinished |
+ kDamagedFinished;
+
+static const uint16 kSubPreppedBit = (1 << 0);
+static const uint16 kWaitingForPlayerBit = (1 << 1);
+
+SubPlatform::SubPlatform(Neighborhood *handler) : GameInteraction(kNoradSubPlatformInteractionID, handler),
+ _platformMovie(kPlatformMonitorID), _platformNotification(kNoradSubPlatformNotificationID, (PegasusEngine *)g_engine) {
+ _neighborhoodNotification = handler->getNeighborhoodNotification();
+}
+
+void SubPlatform::openInteraction() {
+ _stateBits = 0;
+
+ // TODO: These next two lines seem unused?
+ if (GameState.getNoradSubPrepState() == kSubPrepped)
+ _stateBits |= kSubPreppedBit;
+
+ _stateBits |= kWaitingForPlayerBit;
+ _platformMovie.initFromMovieFile("Images/Norad Alpha/Platform Monitor Movie");
+ _platformMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+ _platformMovie.moveElementTo(kNoradPlatformLeft, kNoradPlatformTop);
+ _platformScale = _platformMovie.getScale();
+ _platformMovie.setDisplayOrder(kPlatformOrder);
+ _platformMovie.startDisplaying();
+ _platformCallBack.setNotification(&_platformNotification);
+ _platformCallBack.initCallBack(&_platformMovie, kCallBackAtExtremes);
+
+ _platformNotification.notifyMe(this, kPlatformNotificationFlags, kPlatformNotificationFlags);
+}
+
+void SubPlatform::initInteraction() {
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
+}
+
+void SubPlatform::closeInteraction() {
+ _platformNotification.cancelNotification(this);
+ _platformCallBack.releaseCallBack();
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void SubPlatform::setSoundFXLevel(const uint16 fxLevel) {
+ _platformMovie.setVolume(fxLevel);
+}
+
+void SubPlatform::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ FaderMoveSpec loop1Spec, loop2Spec;
+ ExtraTable::Entry entry;
+
+ Norad *owner = (Norad *)getOwner();
+
+ if (notification == &_platformNotification) {
+ switch (flags) {
+ case kNormalSplashFinished:
+ _platformMovie.stop();
+ switch (GameState.getNoradSubPrepState()) {
+ case kSubNotPrepped:
+ _platformMovie.setSegment(kPrepIncompleteStart * _platformScale, kPrepIncompleteStop * _platformScale);
+ _platformMovie.setTime(kPrepIncompleteStart * _platformScale);
+ _platformCallBack.setCallBackFlag(kPrepIncompleteFinished);
+ _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _platformMovie.start();
+ break;
+ case kSubPrepped:
+ _platformMovie.setSegment(kPrepSubStart * _platformScale, kPrepSubStop * _platformScale);
+ _platformMovie.setTime(kPrepSubStart * _platformScale);
+ _platformCallBack.setCallBackFlag(kPrepSubFinished);
+ _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ owner->startExtraSequence(kNorad19PrepSub, 0, kFilterNoInput);
+ _platformMovie.start();
+ break;
+ case kSubDamaged:
+ // Shouldn't happen.
+ break;
+ }
+ break;
+ case kPrepSubFinished:
+ _platformMovie.stop();
+ _platformMovie.stopDisplaying();
+
+ owner->getExtraEntry(kNorad19ExitToSub, entry);
+
+ loop1Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradWarningVolume,
+ entry.movieEnd - entry.movieStart, 0);
+ loop1Spec.insertFaderKnot(4560, kNoradWarningVolume);
+ loop1Spec.insertFaderKnot(5080, 0);
+
+ loop2Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradSuckWindVolume,
+ entry.movieEnd - entry.movieStart, 0);
+ loop1Spec.insertFaderKnot(4560, kNoradSuckWindVolume);
+ loop1Spec.insertFaderKnot(5080, 0);
+
+ owner->startExtraSequence(kNorad19ExitToSub, kExtraCompletedFlag, kFilterNoInput);
+
+ owner->startLoop1Fader(loop1Spec);
+ owner->startLoop2Fader(loop2Spec);
+ break;
+ case kPrepIncompleteFinished:
+ ((NoradAlpha *)owner)->setSubPrepFailed(true);
+ g_AIArea->checkMiddleArea();
+ // Fall through...
+ case kDamagedFinished:
+ _platformMovie.stop();
+ _platformMovie.hide();
+ _stateBits |= kWaitingForPlayerBit;
+ allowInput(true);
+ break;
+ }
+ } else if (notification == _neighborhoodNotification) {
+ allowInput(true);
+ ((PegasusEngine *)g_engine)->jumpToNewEnvironment(kNoradSubChaseID, kNoRoomID, kNoDirection);
+ GameState.setScoringEnteredSub(true);
+ }
+}
+
+void SubPlatform::activateHotspots() {
+ if (_stateBits & kWaitingForPlayerBit)
+ g_allHotspots.activateOneHotspot(kNorad19ActivateMonitorSpotID);
+
+ GameInteraction::activateHotspots();
+}
+
+void SubPlatform::clickInHotspot(const Input &input, const Hotspot *spot) {
+ if (spot->getObjectID() == kNorad19ActivateMonitorSpotID) {
+ if (GameState.getNoradSubPrepState() == kSubDamaged) {
+ _platformMovie.setSegment(kDamagedStart * _platformScale, kDamagedStop * _platformScale);
+ _platformMovie.setTime(kDamagedStart * _platformScale);
+ _platformCallBack.setCallBackFlag(kDamagedFinished);
+ } else {
+ _platformMovie.setSegment(kNormalSplashStart * _platformScale, kNormalSplashStop * _platformScale);
+ _platformMovie.setTime(kNormalSplashStart * _platformScale);
+ _platformCallBack.setCallBackFlag(kNormalSplashFinished);
+ }
+
+ _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ _platformMovie.show();
+ _platformMovie.start();
+ _platformMovie.redrawMovieWorld();
+
+ _stateBits &= ~kWaitingForPlayerBit;
+
+ allowInput(false);
+ } else {
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/subplatform.h b/engines/pegasus/neighborhood/norad/subplatform.h
new file mode 100644
index 0000000000..82e86ecae2
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/subplatform.h
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class SubPlatform : public GameInteraction, public NotificationReceiver {
+public:
+ SubPlatform(Neighborhood *);
+ virtual ~SubPlatform() {}
+
+ virtual void setSoundFXLevel(const uint16);
+
+protected:
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+
+ Movie _platformMovie;
+ TimeScale _platformScale;
+ Notification _platformNotification;
+ NotificationCallBack _platformCallBack;
+ Notification *_neighborhoodNotification;
+ uint16 _stateBits;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp
new file mode 100644
index 0000000000..814d7717de
--- /dev/null
+++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp
@@ -0,0 +1,689 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/compass.h"
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_action.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/ai/ai_condition.h"
+#include "pegasus/ai/ai_rule.h"
+#include "pegasus/neighborhood/prehistoric/prehistoric.h"
+
+namespace Pegasus {
+
+static const int16 s_prehistoricCompass[kPrehistoric25 + 1][4] = {
+ { 0, 170, 90, 270 }, // kPrehistoric01
+ { 0, 180, 90, 270 }, // kPrehistoric02
+ { 10, 180, 90, 270 }, // kPrehistoric03
+ { 10, 190, 90, 270 }, // kPrehistoric04
+ { 10, 195, 90, 270 }, // kPrehistoric05
+ { 20, 210, 90, 270 }, // kPrehistoric06
+ { 20, 200, 130, 276 }, // kPrehistoric07
+ { 20, 176, 110, 260 }, // kPrehistoric08
+ { 20, 200, 100, 270 }, // kPrehistoric09
+ { 14, 186, 100, 280 }, // kPrehistoric10
+ { 26, 206, 116, 296 }, // kPrehistoric11
+ { 60, 226, 140, 320 }, // kPrehistoric12
+ { 0, 180, 80, 270 }, // kPrehistoric13
+ { 14, 200, 106, 286 }, // kPrehistoric14
+ { -10, 174, 80, 260 }, // kPrehistoric15
+ { 54, 236, 140, 210 }, // kPrehistoric16
+ { -24, 160, 70, 250 }, // kPrehistoric17
+ { 26, 206, 140, 296 }, // kPrehistoric18
+ { -16, 160, 70, 250 }, // kPrehistoric19
+ { -16, 160, 70, 250 }, // kPrehistoric20
+ { -10, 160, 90, 250 }, // kPrehistoric21
+ { -20, 160, 70, 244 }, // kPrehistoric22
+ { -20, 160, 70, 244 }, // kPrehistoric22North
+ { 60, 234, 150, 330 }, // kPrehistoric23
+ { 50, 230, 140, 320 }, // kPrehistoric24
+ { 60, 240, 140, 330 } // kPrehistoric25
+};
+
+static const TimeValue kPrehistoricFlashlightClickIn = 0;
+static const TimeValue kPrehistoricFlashlightClickOut = 138;
+
+static const TimeValue kPrehistoricBumpIntoWallIn = 138;
+static const TimeValue kPrehistoricBumpIntoWallOut = 291;
+
+static const TimeValue kBridgeRetractIn = 291;
+static const TimeValue kBridgeRetractOut = 1499;
+
+static const TimeValue kPrehistoricWarningTimeLimit = kTenMinutes;
+
+Prehistoric::Prehistoric(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Prehistoric", kPrehistoricID) {
+ setIsItemTaken(kHistoricalLog);
+}
+
+uint16 Prehistoric::getDateResID() const {
+ return kDatePrehistoricID;
+}
+
+void Prehistoric::init() {
+ Neighborhood::init();
+
+ // Forces a stop so the flashlight can turn off...
+ forceStridingStop(kPrehistoric12, kSouth, kNoAlternateID);
+}
+
+void Prehistoric::start() {
+ if (g_energyMonitor) {
+ g_energyMonitor->stopEnergyDraining();
+ g_energyMonitor->restoreLastEnergyValue();
+ _vm->resetEnergyDeathReason();
+ g_energyMonitor->startEnergyDraining();
+ }
+
+ Neighborhood::start();
+}
+
+class FinishPrehistoricAction : public AIPlayMessageAction {
+public:
+ FinishPrehistoricAction() : AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false) {}
+ ~FinishPrehistoricAction() {}
+
+ void performAIAction(AIRule *);
+
+};
+
+void FinishPrehistoricAction::performAIAction(AIRule *rule) {
+ AIPlayMessageAction::performAIAction(rule);
+ ((PegasusEngine *)g_engine)->die(kPlayerWonGame);
+}
+
+void Prehistoric::setUpAIRules() {
+ Neighborhood::setUpAIRules();
+
+ if (g_AIArea) {
+ if (_vm->isDemo()) {
+ FinishPrehistoricAction *doneAction = new FinishPrehistoricAction();
+ AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog);
+ AIRule *rule = new AIRule(hasLogCondition, doneAction);
+ g_AIArea->addAIRule(rule);
+ } else {
+ AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP1NB", false);
+ AILocationCondition *locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kPrehistoric16, kNorth));
+ AIRule *rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kPrehistoric01, kSouth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kPrehistoric08, kEast));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kPrehistoric25, kWest));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP16NB", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kPrehistoric23, kNorth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP18NB", false);
+ AITimerCondition *timerCondition = new AITimerCondition(kPrehistoricWarningTimeLimit, 1, true);
+ rule = new AIRule(timerCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false);
+ AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog);
+ rule = new AIRule(hasLogCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+ }
+ }
+}
+
+TimeValue Prehistoric::getViewTime(const RoomID room, const DirectionConstant direction) {
+ ExtraTable::Entry extra;
+ uint32 extraID = 0xffffffff;
+
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kPrehistoric02, kSouth):
+ if (!GameState.getPrehistoricSeenTimeStream()) {
+ getExtraEntry(kPreArrivalFromTSA, extra);
+ return extra.movieStart;
+ }
+ break;
+ case MakeRoomView(kPrehistoric25, kEast):
+ if (_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) {
+ if (_vm->itemInLocation(kHistoricalLog, kPrehistoricID, kPrehistoric25, kEast))
+ extraID = kPre25EastViewWithLog;
+ else
+ extraID = kPre25EastViewNoLog;
+ }
+ break;
+ }
+
+ if (extraID == 0xffffffff)
+ return Neighborhood::getViewTime(room, direction);
+
+ getExtraEntry(extraID, extra);
+ return extra.movieEnd - 1;
+}
+
+
+void Prehistoric::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) {
+ Neighborhood::findSpotEntry(room, direction, flags, entry);
+
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kPrehistoric01, kSouth):
+ case MakeRoomView(kPrehistoric25, kSouth):
+ entry.clear();
+ break;
+ case MakeRoomView(kPrehistoric01, kEast):
+ if (GameState.getPrehistoricSeenFlyer1())
+ entry.clear();
+ else
+ GameState.setPrehistoricSeenFlyer1(true);
+ break;
+ case MakeRoomView(kPrehistoric08, kEast):
+ if (GameState.getPrehistoricSeenFlyer2())
+ entry.clear();
+ else
+ GameState.setPrehistoricSeenFlyer2(true);
+ break;
+ }
+}
+
+int16 Prehistoric::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
+ if (room == kPrehistoricDeath)
+ return g_compass->getFaderValue();
+
+ return s_prehistoricCompass[room][dir];
+}
+
+void Prehistoric::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) {
+ uint32 angle;
+ Neighborhood::getExitCompassMove(exitEntry, compassMove);
+
+ switch (MakeRoomView(exitEntry.room, exitEntry.direction)) {
+ case MakeRoomView(kPrehistoric01, kNorth):
+ compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 2, -10);
+ break;
+ case MakeRoomView(kPrehistoric06, kEast):
+ compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4, 95);
+ compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4 * 1, 100);
+ break;
+ case MakeRoomView(kPrehistoric18, kEast):
+ if (getCurrentAlternate() == kAltPrehistoricBridgeSet) {
+ compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 11, 145);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 26, 145);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 39, 148);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 114, 140);
+ } else {
+ compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 10, 140);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 16, 145);
+ compassMove.insertFaderKnot(exitEntry.movieEnd, 145);
+ }
+ break;
+ case MakeRoomView(kPrehistoric23, kWest):
+ angle = compassMove.getNthKnotValue(0);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 17, angle);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 32, angle - 90);
+ compassMove.insertFaderKnot(exitEntry.movieEnd, angle - 90);
+ break;
+ }
+}
+
+void Prehistoric::turnTo(const DirectionConstant newDirection) {
+ setCurrentAlternate(kAltPrehistoricNormal);
+ _privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, false);
+ Neighborhood::turnTo(newDirection);
+
+ Item *keyCard;
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kPrehistoric18, kEast):
+ zoomToVault();
+ break;
+ case MakeRoomView(kPrehistoric18, kNorth):
+ case MakeRoomView(kPrehistoric18, kSouth):
+ if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) {
+ playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut);
+ _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false);
+ loadAmbientLoops();
+ }
+ // fall through
+ case MakeRoomView(kPrehistoric25, kEast):
+ setCurrentActivation(kActivationVaultClosed);
+ break;
+ case MakeRoomView(kPrehistoric16, kNorth):
+ case MakeRoomView(kPrehistoric21, kWest):
+ keyCard = _vm->getAllItems().findItemByID(kKeyCard);
+ if (keyCard->getItemState() == kFlashlightOff) {
+ keyCard->setItemState(kFlashlightOn);
+ playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut);
+ }
+ break;
+ case MakeRoomView(kPrehistoric16, kEast):
+ case MakeRoomView(kPrehistoric16, kWest):
+ case MakeRoomView(kPrehistoric21, kNorth):
+ case MakeRoomView(kPrehistoric21, kSouth):
+ keyCard = _vm->getAllItems().findItemByID(kKeyCard);
+ if (keyCard->getItemState() == kFlashlightOn) {
+ keyCard->setItemState(kFlashlightOff);
+ playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut);
+ }
+ break;
+ }
+}
+
+void Prehistoric::zoomToVault() {
+ if (!GameState.getPrehistoricSeenBridgeZoom())
+ startExtraSequence(kPre18EastZoom, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void Prehistoric::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kPrehistoric08, kEast):
+ case MakeRoomView(kPrehistoric18, kSouth):
+ case MakeRoomView(kPrehistoric16, kNorth):
+ case MakeRoomView(kPrehistoric21, kNorth):
+ case MakeRoomView(kPrehistoric25, kNorth):
+ makeContinuePoint();
+ break;
+ }
+}
+
+void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction) {
+ Item *keyCard;
+
+ if (MakeRoomView(room, direction) == MakeRoomView(kPrehistoric25, kEast) &&
+ _privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) {
+ _navMovie.stop();
+ playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut);
+ _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false);
+ }
+
+ Neighborhood::arriveAt(room, direction);
+
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kPrehistoricDeath, kNorth):
+ case MakeRoomView(kPrehistoricDeath, kSouth):
+ case MakeRoomView(kPrehistoricDeath, kEast):
+ case MakeRoomView(kPrehistoricDeath, kWest):
+ if (GameState.getLastRoom() == kPrehistoric23)
+ die(kDeathEatenByDinosaur);
+ else
+ die(kDeathFallOffCliff);
+ break;
+ case MakeRoomView(kPrehistoric02, kSouth):
+ if (!GameState.getPrehistoricSeenTimeStream()) {
+ GameState.setPrehistoricTriedToExtendBridge(false);
+ GameState.setPrehistoricSeenFlyer1(false);
+ GameState.setPrehistoricSeenFlyer2(false);
+ GameState.setPrehistoricSeenBridgeZoom(false);
+ GameState.setPrehistoricBreakerThrown(false);
+ startExtraSequence(kPreArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case MakeRoomView(kPrehistoric18, kEast):
+ zoomToVault();
+ break;
+ case MakeRoomView(kPrehistoric16, kNorth):
+ keyCard = _vm->getAllItems().findItemByID(kKeyCard);
+
+ if (keyCard->getItemState() == kFlashlightOff) {
+ keyCard->setItemState(kFlashlightOn);
+ playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut);
+ }
+
+ if (g_AIArea)
+ g_AIArea->checkRules();
+ break;
+ case MakeRoomView(kPrehistoric01, kSouth):
+ case MakeRoomView(kPrehistoric23, kNorth):
+ if (g_AIArea)
+ g_AIArea->checkRules();
+ break;
+ case MakeRoomView(kPrehistoric08, kSouth):
+ case MakeRoomView(kPrehistoric10, kSouth):
+ case MakeRoomView(kPrehistoric12, kSouth):
+ case MakeRoomView(kPrehistoric13, kNorth):
+ case MakeRoomView(kPrehistoric14, kSouth):
+ case MakeRoomView(kPrehistoric15, kNorth):
+ case MakeRoomView(kPrehistoric16, kSouth):
+ case MakeRoomView(kPrehistoric17, kNorth):
+ case MakeRoomView(kPrehistoric18, kSouth):
+ case MakeRoomView(kPrehistoric19, kNorth):
+ case MakeRoomView(kPrehistoric20, kNorth):
+ case MakeRoomView(kPrehistoric21, kEast):
+ keyCard = _vm->getAllItems().findItemByID(kKeyCard);
+
+ if (keyCard->getItemState() == kFlashlightOn) {
+ keyCard->setItemState(kFlashlightOff);
+ playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut);
+ }
+ break;
+ case MakeRoomView(kPrehistoric25, kEast):
+ setCurrentActivation(kActivationVaultClosed);
+ break;
+ }
+}
+
+void Prehistoric::loadAmbientLoops() {
+ RoomID room = GameState.getCurrentRoom();
+
+ switch (room) {
+ case kPrehistoric02:
+ // 1/4 volume.
+ if (GameState.getPrehistoricSeenTimeStream())
+ loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64);
+ break;
+ case kPrehistoric01:
+ case kPrehistoric03:
+ case kPrehistoric04:
+ case kPrehistoric05:
+ case kPrehistoric06:
+ case kPrehistoric07:
+ case kPrehistoric09:
+ case kPrehistoric11:
+ case kPrehistoric13:
+ case kPrehistoric15:
+ case kPrehistoric17:
+ case kPrehistoric19:
+ case kPrehistoric20:
+ // 1/4 volume.
+ loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64);
+ break;
+ case kPrehistoric08:
+ case kPrehistoric10:
+ case kPrehistoric12:
+ case kPrehistoric14:
+ case kPrehistoric16:
+ case kPrehistoric18:
+ case kPrehistoric21:
+ // 3/16 volume.
+ loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 48);
+ break;
+ case kPrehistoric25:
+ // 1/8 volume.
+ loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 32);
+ break;
+ case kPrehistoric22:
+ case kPrehistoric22North:
+ case kPrehistoric23:
+ case kPrehistoric24:
+ case kPrehistoricDeath:
+ // 0 volume.
+ loadLoopSound1("");
+ break;
+ }
+
+ switch (room) {
+ case kPrehistoric02:
+ case kPrehistoric03:
+ case kPrehistoric04:
+ case kPrehistoric05:
+ case kPrehistoric06:
+ case kPrehistoric07:
+ case kPrehistoric08:
+ case kPrehistoric09:
+ case kPrehistoric10:
+ case kPrehistoric11:
+ case kPrehistoric12:
+ case kPrehistoric13:
+ case kPrehistoric14:
+ case kPrehistoric15:
+ case kPrehistoric16:
+ case kPrehistoric17:
+ case kPrehistoric19:
+ case kPrehistoric20:
+ case kPrehistoric21:
+ case kPrehistoricDeath:
+ loadLoopSound2("");
+ break;
+ case kPrehistoric01:
+ case kPrehistoric25:
+ loadLoopSound2("Sounds/Prehistoric/VolcLoop.22K.AIFF", 64);
+ break;
+ case kPrehistoric18:
+ if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag))
+ loadLoopSound2("Sounds/Prehistoric/P18EAL00.22k.AIFF", 0x100, 0, 0);
+ else
+ loadLoopSound2("");
+ break;
+ case kPrehistoric23:
+ case kPrehistoric24:
+ case kPrehistoric22:
+ case kPrehistoric22North:
+ loadLoopSound2("Sounds/Prehistoric/P24NAL00.22k.AIFF", 64);
+ break;
+ }
+}
+
+void Prehistoric::activateHotspots() {
+ Neighborhood::activateHotspots();
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kPrehistoric18, kEast):
+ if (!_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag))
+ _vm->getAllHotspots().activateOneHotspot(kPre18EastSpotID);
+ break;
+ case MakeRoomView(kPrehistoric22North, kNorth):
+ _vm->getAllHotspots().activateOneHotspot(kPre22NorthBreakerSpotID);
+ break;
+ }
+}
+
+void Prehistoric::clickInHotspot(const Input &input, const Hotspot *spot) {
+ switch (spot->getObjectID()) {
+ case kPre18EastSpotID:
+ if (GameState.getPrehistoricBreakerThrown())
+ startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kPre18EastBridgeOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kPre22NorthBreakerSpotID:
+ startExtraSequence(kPre22ThrowBreaker, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ Neighborhood::clickInHotspot(input, spot);
+ break;
+ }
+}
+
+void Prehistoric::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ Neighborhood::receiveNotification(notification, flags);
+
+ if ((flags & kExtraCompletedFlag) != 0) {
+ _interruptionFilter = kFilterAllInput;
+
+ switch (_lastExtra) {
+ case kPreArrivalFromTSA:
+ GameState.setPrehistoricSeenTimeStream(true);
+ loadAmbientLoops();
+ makeContinuePoint();
+ break;
+ case kPre18EastZoom:
+ startExtraSequence(kPre18EastZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kPre18EastZoomOut:
+ GameState.setPrehistoricSeenBridgeZoom(true);
+ break;
+ case kPre18EastBridgeOn:
+ _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, true);
+ setCurrentAlternate(kAltPrehistoricBridgeSet);
+ GameState.setPrehistoricTriedToExtendBridge(false);
+ loadAmbientLoops();
+ GameState.setScoringExtendedBridge(true);
+ break;
+ case kPre18EastBridgeOut:
+ GameState.setPrehistoricTriedToExtendBridge(true);
+ if (g_AIArea)
+ g_AIArea->checkMiddleArea();
+ break;
+ case kPre22ThrowBreaker:
+ GameState.setPrehistoricBreakerThrown(true);
+ GameState.setScoringThrewBreaker(true);
+ break;
+ case kPre25EastUnlockingVaultNoLog:
+ case kPre25EastUnlockingVaultWithLog:
+ _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kJourneymanKey));
+ break;
+ }
+ }
+
+ g_AIArea->checkMiddleArea();
+}
+
+Common::String Prehistoric::getBriefingMovie() {
+ Common::String movieName = Neighborhood::getBriefingMovie();
+
+ if (movieName.empty())
+ movieName = "Images/AI/Prehistoric/XPE";
+
+ return movieName;
+}
+
+Common::String Prehistoric::getEnvScanMovie() {
+ Common::String movieName = Neighborhood::getEnvScanMovie();
+
+ if (movieName.empty()) {
+ if (!_vm->isDemo()) {
+ switch (GameState.getCurrentRoom()) {
+ case kPrehistoric16:
+ case kPrehistoric23:
+ case kPrehistoric24:
+ return "Images/AI/Prehistoric/XP7WB";
+ }
+ }
+
+ return "Images/AI/Prehistoric/XP17NB";
+ }
+
+ return movieName;
+}
+
+uint Prehistoric::getNumHints() {
+ uint numHints = Neighborhood::getNumHints();
+
+ if (numHints == 0) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kPrehistoric18, kEast):
+ if (!GameState.getPrehistoricBreakerThrown() && GameState.getPrehistoricTriedToExtendBridge() &&
+ !_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag))
+ numHints = 1;
+ break;
+ case MakeRoomView(kPrehistoric25, kEast):
+ if (!_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag))
+ numHints = 1;
+ break;
+ }
+ }
+
+ return numHints;
+}
+
+Common::String Prehistoric::getHintMovie(uint hintNum) {
+ Common::String movieName = Neighborhood::getHintMovie(hintNum);
+
+ if (movieName.empty()) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kPrehistoric18, kEast):
+ return "Images/AI/Prehistoric/XP18WD";
+ case MakeRoomView(kPrehistoric25, kEast):
+ return "Images/AI/Globals/XGLOB1A";
+ }
+ }
+
+ return movieName;
+}
+
+bool Prehistoric::canSolve() {
+ return GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric18, kEast) &&
+ !GameState.getPrehistoricBreakerThrown() &&
+ GameState.getPrehistoricTriedToExtendBridge() &&
+ !_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag);
+}
+
+void Prehistoric::doSolve() {
+ GameState.setPrehistoricBreakerThrown(true);
+ startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput);
+}
+
+Hotspot *Prehistoric::getItemScreenSpot(Item *item, DisplayElement *element) {
+ if (item->getObjectID() == kHistoricalLog)
+ return _vm->getAllHotspots().findHotspotByID(kPrehistoricHistoricalLogSpotID);
+
+ return Neighborhood::getItemScreenSpot(item, element);
+}
+
+void Prehistoric::pickedUpItem(Item *item) {
+ switch (item->getObjectID()) {
+ case kHistoricalLog:
+ GameState.setScoringGotHistoricalLog(true);
+ break;
+ }
+
+ Neighborhood::pickedUpItem(item);
+}
+
+void Prehistoric::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
+ switch (item->getObjectID()) {
+ case kJourneymanKey:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+
+ if (GameState.isTakenItemID(kHistoricalLog))
+ startExtraLongSequence(kPre25EastUnlockingVaultNoLog, kPre25EastVaultOpenNoLog, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraLongSequence(kPre25EastUnlockingVaultWithLog, kPre25EastVaultOpenWithLog, kExtraCompletedFlag, kFilterNoInput);
+
+ _privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, true);
+ setCurrentActivation(kActivationVaultOpen);
+ break;
+ default:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ }
+}
+
+void Prehistoric::bumpIntoWall() {
+ requestSpotSound(kPrehistoricBumpIntoWallIn, kPrehistoricBumpIntoWallOut, kFilterAllInput, 0);
+ Neighborhood::bumpIntoWall();
+}
+
+Common::String Prehistoric::getNavMovieName() {
+ return "Images/Prehistoric/Prehistoric.movie";
+}
+
+Common::String Prehistoric::getSoundSpotsName() {
+ return "Sounds/Prehistoric/Prehistoric Spots";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.h b/engines/pegasus/neighborhood/prehistoric/prehistoric.h
new file mode 100644
index 0000000000..17f9993014
--- /dev/null
+++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.h
@@ -0,0 +1,158 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_PREHISTORIC_H
+#define PEGASUS_NEIGHBORHOOD_PREHISTORIC_H
+
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+static const TimeScale kPrehistoricMovieScale = 600;
+static const TimeScale kPrehistoricFramesPerSecond = 15;
+static const TimeScale kPrehistoricFrameDuration = 40;
+
+// Alternate IDs.
+
+static const AlternateID kAltPrehistoricNormal = 0;
+static const AlternateID kAltPrehistoricBridgeSet = 1;
+
+// Room IDs.
+
+static const RoomID kPrehistoric01 = 0;
+static const RoomID kPrehistoric02 = 1;
+static const RoomID kPrehistoric03 = 2;
+static const RoomID kPrehistoric04 = 3;
+static const RoomID kPrehistoric05 = 4;
+static const RoomID kPrehistoric06 = 5;
+static const RoomID kPrehistoric07 = 6;
+static const RoomID kPrehistoric08 = 7;
+static const RoomID kPrehistoric09 = 8;
+static const RoomID kPrehistoric10 = 9;
+static const RoomID kPrehistoric11 = 10;
+static const RoomID kPrehistoric12 = 11;
+static const RoomID kPrehistoric13 = 12;
+static const RoomID kPrehistoric14 = 13;
+static const RoomID kPrehistoric15 = 14;
+static const RoomID kPrehistoric16 = 15;
+static const RoomID kPrehistoric17 = 16;
+static const RoomID kPrehistoric18 = 17;
+static const RoomID kPrehistoric19 = 18;
+static const RoomID kPrehistoric20 = 19;
+static const RoomID kPrehistoric21 = 20;
+static const RoomID kPrehistoric22 = 21;
+static const RoomID kPrehistoric22North = 22;
+static const RoomID kPrehistoric23 = 23;
+static const RoomID kPrehistoric24 = 24;
+static const RoomID kPrehistoric25 = 25;
+static const RoomID kPrehistoricDeath = 26;
+
+// Hot Spot Activation IDs.
+
+static const HotSpotActivationID kActivationVaultClosed = 1;
+static const HotSpotActivationID kActivationVaultOpen = 2;
+
+// Hot Spot IDs.
+
+static const HotSpotID kPre18EastSpotID = 5000;
+static const HotSpotID kPre22NorthSpotID = 5001;
+static const HotSpotID kPre22NorthOutSpotID = 5002;
+static const HotSpotID kPre22NorthBreakerSpotID = 5003;
+static const HotSpotID kPrehistoricKeyDropSpotID = 5004;
+static const HotSpotID kPrehistoricHistoricalLogSpotID = 5005;
+
+// Extra sequence IDs.
+
+static const ExtraID kPreArrivalFromTSA = 0;
+static const ExtraID kPre18EastBridgeOut = 1;
+static const ExtraID kPre18EastBridgeOn = 2;
+static const ExtraID kPre18EastZoom = 3;
+static const ExtraID kPre18EastZoomOut = 4;
+static const ExtraID kPre22ThrowBreaker = 5;
+static const ExtraID kPre25EastUnlockingVaultWithLog = 6;
+static const ExtraID kPre25EastVaultOpenWithLog = 7;
+static const ExtraID kPre25EastViewWithLog = 8;
+static const ExtraID kPre25EastUnlockingVaultNoLog = 9;
+static const ExtraID kPre25EastVaultOpenNoLog = 10;
+static const ExtraID kPre25EastViewNoLog = 11;
+
+class PegasusEngine;
+
+class Prehistoric : public Neighborhood {
+public:
+ Prehistoric(InputHandler *, PegasusEngine *);
+ virtual ~Prehistoric() {}
+
+ virtual uint16 getDateResID() const;
+ virtual void init();
+
+ virtual void arriveAt(const RoomID, const DirectionConstant);
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+ Common::String getBriefingMovie();
+ Common::String getEnvScanMovie();
+ uint getNumHints();
+ Common::String getHintMovie(uint);
+
+ Hotspot *getItemScreenSpot(Item *, DisplayElement *);
+ void dropItemIntoRoom(Item *, Hotspot *);
+ void pickedUpItem(Item *);
+
+ void start();
+
+ void bumpIntoWall();
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+ bool canSolve();
+ void doSolve();
+
+protected:
+ enum {
+ kPrehistoricPrivateVaultOpenFlag,
+ kPrehistoricPrivateExtendedBridgeFlag,
+ kNumPrehistoricPrivateFlags
+ };
+
+ void setUpAIRules();
+ int16 getStaticCompassAngle(const RoomID, const DirectionConstant);
+ void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &);
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+ void turnTo(const DirectionConstant);
+ void zoomToVault();
+ TimeValue getViewTime(const RoomID, const DirectionConstant);
+ void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &);
+
+ void loadAmbientLoops();
+
+ FlagsArray<byte, kNumPrehistoricPrivateFlags> _privateFlags;
+
+ Common::String getNavMovieName();
+ Common::String getSoundSpotsName();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/spot.cpp b/engines/pegasus/neighborhood/spot.cpp
new file mode 100644
index 0000000000..f285bf9bc2
--- /dev/null
+++ b/engines/pegasus/neighborhood/spot.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "pegasus/neighborhood/spot.h"
+
+namespace Pegasus {
+
+void SpotTable::loadFromStream(Common::SeekableReadStream *stream) {
+ uint32 count = stream->readUint32BE();
+ _entries.resize(count);
+
+ for (uint32 i = 0; i < count; i++) {
+ _entries[i].room = stream->readUint16BE();
+ _entries[i].direction = stream->readByte();
+ _entries[i].srcFlags = stream->readByte();
+ _entries[i].altCode = stream->readByte();
+ stream->readByte(); // alignment
+ _entries[i].movieStart = stream->readUint32BE();
+ _entries[i].movieEnd = stream->readUint32BE();
+ _entries[i].dstFlags = stream->readByte();
+ stream->readByte(); // alignment
+ debug(0, "Spot[%d]: %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction,
+ _entries[i].srcFlags, _entries[i].altCode, _entries[i].movieStart,
+ _entries[i].movieEnd, _entries[i].dstFlags);
+ }
+}
+
+void SpotTable::clear() {
+ _entries.clear();
+}
+
+// Two SpotTable::Entries are equal if
+// In addition to having their rooms, directions and alt codes identical...
+// They are both either loops or once only animations AND
+// They overlap in at least one of the on arrival, on turn and on door open bits.
+SpotTable::Entry SpotTable::findEntry(RoomID room, DirectionConstant direction, SpotFlags srcFlags, AlternateID altCode) {
+ for (uint32 i = 0; i < _entries.size(); i++)
+ if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode && (_entries[i].srcFlags & kSpotLoopsMask) == (srcFlags & kSpotLoopsMask) && ((_entries[i].srcFlags & srcFlags) & kSpotTriggers) != 0)
+ return _entries[i];
+
+ return Entry();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/spot.h b/engines/pegasus/neighborhood/spot.h
new file mode 100644
index 0000000000..a985420b7c
--- /dev/null
+++ b/engines/pegasus/neighborhood/spot.h
@@ -0,0 +1,97 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_SPOT_H
+#define PEGASUS_NEIGHBORHOOD_SPOT_H
+
+#include "common/array.h"
+#include "common/endian.h"
+
+#include "pegasus/constants.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+typedef byte SpotFlags;
+
+enum {
+ kSpotLoopsBit, // Loop or once only?
+ kSpotOnArrivalBit,
+ kSpotOnTurnBit,
+ kSpotOnDoorOpenBit
+};
+
+static const SpotFlags kNoSpotFlags = 0;
+static const SpotFlags kSpotLoopsMask = 1 << kSpotLoopsBit;
+static const SpotFlags kSpotOnArrivalMask = 1 << kSpotOnArrivalBit;
+static const SpotFlags kSpotOnTurnMask = 1 << kSpotOnTurnBit;
+static const SpotFlags kSpotOnDoorOpenMask = 1 << kSpotOnDoorOpenBit;
+
+static const SpotFlags kSpotTriggers = kSpotOnArrivalMask | kSpotOnTurnMask | kSpotOnDoorOpenMask;
+
+class SpotTable {
+public:
+ SpotTable() {}
+ ~SpotTable() {}
+
+ static uint32 getResTag() { return MKTAG('S', 'p', 'o', 't'); }
+
+ void loadFromStream(Common::SeekableReadStream *stream);
+ void clear();
+
+ struct Entry {
+ Entry() { clear(); }
+ bool isEmpty() { return movieStart == 0xffffffff; }
+ void clear() {
+ room = kNoRoomID;
+ direction = kNoDirection;
+ srcFlags = kNoSpotFlags;
+ altCode = kNoAlternateID;
+ movieStart = 0xffffffff;
+ movieEnd = 0xffffffff;
+ dstFlags = kNoSpotFlags;
+ }
+
+ RoomID room;
+ DirectionConstant direction;
+ SpotFlags srcFlags;
+ AlternateID altCode;
+ TimeValue movieStart;
+ TimeValue movieEnd;
+ SpotFlags dstFlags;
+ };
+
+ Entry findEntry(RoomID room, DirectionConstant direction, SpotFlags srcFlags, AlternateID altCode);
+
+private:
+ Common::Array<Entry> _entries;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.cpp b/engines/pegasus/neighborhood/tsa/fulltsa.cpp
new file mode 100644
index 0000000000..b598841b45
--- /dev/null
+++ b/engines/pegasus/neighborhood/tsa/fulltsa.cpp
@@ -0,0 +1,3023 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/cursor.h"
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/aichip.h"
+#include "pegasus/items/biochips/opticalchip.h"
+#include "pegasus/neighborhood/caldoria/caldoria.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/prehistoric/prehistoric.h"
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/tsa/fulltsa.h"
+#include "pegasus/neighborhood/wsc/wsc.h"
+
+namespace Pegasus {
+
+// TSA PICTs:
+
+static const ResIDType kTBPCloseBoxPICTID = 800;
+static const ResIDType kTBPRewindPICTID = 801;
+static const ResIDType kUnresolvedPICTID = 802;
+static const ResIDType kResolvedPICTID = 803;
+static const ResIDType kJumpMenuPICTID = 804;
+static const ResIDType kJumpMenuHilitedPICTID = 805;
+static const ResIDType kExitPICTID = 806;
+static const ResIDType kExitHilitedPICTID = 807;
+static const ResIDType kLeftRipPICTID = 808;
+static const ResIDType kComparisonCloseBoxPICTID = 809;
+static const ResIDType kComparisonLeftRewindPICTID = 810;
+static const ResIDType kComparisonRightRewindPICTID = 811;
+static const ResIDType kComparisonHiliteNoradPICTID = 812;
+static const ResIDType kComparisonHiliteMarsPICTID = 813;
+static const ResIDType kComparisonHiliteCaldoriaPICTID = 814;
+static const ResIDType kComparisonHiliteWSCPICTID = 815;
+static const ResIDType kComparisonChancesNoradPICTID = 816;
+static const ResIDType kComparisonChancesMarsPICTID = 817;
+static const ResIDType kComparisonChancesCaldoriaPICTID = 818;
+static const ResIDType kComparisonChancesWSCPICTID = 819;
+static const ResIDType kRedirectionCCRolloverPICTID = 820;
+static const ResIDType kRedirectionRRRolloverPICTID = 821;
+static const ResIDType kRedirectionFDRolloverPICTID = 822;
+static const ResIDType kRedirectionCCDoorPICTID = 823;
+static const ResIDType kRedirectionRRDoorPICTID = 824;
+static const ResIDType kRedirectionFDDoorPICTID = 825;
+static const ResIDType kRedirectionSecuredPICTID = 826;
+static const ResIDType kRedirectionNewTargetPICTID = 827;
+static const ResIDType kRedirectionClosePICTID = 828;
+
+static const int16 kCompassShift = 15;
+
+static const TimeScale kFullTSAMovieScale = 600;
+static const TimeScale kFullTSAFramesPerSecond = 15;
+static const TimeScale kFullTSAFrameDuration = 40;
+
+// Alternate IDs.
+static const AlternateID kAltTSANormal = 0;
+static const AlternateID kAltTSARobotsAtReadyRoom = 1;
+static const AlternateID kAltTSARobotsAtFrontDoor = 2;
+static const AlternateID kAltTSARedAlert = 3;
+
+// Room IDs.
+static const RoomID kTSA01 = 1;
+static const RoomID kTSA02 = 2;
+static const RoomID kTSA03 = 3;
+static const RoomID kTSA04 = 4;
+static const RoomID kTSA05 = 5;
+static const RoomID kTSA0A = 6;
+static const RoomID kTSA06 = 7;
+static const RoomID kTSA07 = 8;
+static const RoomID kTSA08 = 9;
+static const RoomID kTSA09 = 10;
+static const RoomID kTSA10 = 11;
+static const RoomID kTSA11 = 12;
+static const RoomID kTSA12 = 13;
+static const RoomID kTSA13 = 14;
+static const RoomID kTSA14 = 15;
+static const RoomID kTSA15 = 16;
+static const RoomID kTSA16 = 17;
+static const RoomID kTSA17 = 18;
+static const RoomID kTSA18 = 19;
+static const RoomID kTSA19 = 20;
+static const RoomID kTSA0B = 21;
+static const RoomID kTSA21Cyan = 22;
+static const RoomID kTSA22Cyan = 23;
+static const RoomID kTSA23Cyan = 24;
+static const RoomID kTSA24Cyan = 25;
+static const RoomID kTSA25Cyan = 26;
+static const RoomID kTSA21Red = 27;
+static const RoomID kTSA23Red = 29;
+static const RoomID kTSA24Red = 30;
+static const RoomID kTSA25Red = 31;
+static const RoomID kTSA26 = 32;
+static const RoomID kTSA27 = 33;
+static const RoomID kTSA28 = 34;
+static const RoomID kTSA29 = 35;
+static const RoomID kTSA30 = 36;
+static const RoomID kTSA31 = 37;
+static const RoomID kTSA32 = 38;
+static const RoomID kTSA33 = 39;
+static const RoomID kTSA34 = 40;
+static const RoomID kTSA35 = 41;
+static const RoomID kTSADeathRoom = 43;
+
+// Hot Spot Activation IDs.
+static const HotSpotActivationID kActivateTSAReadyForCard = 1;
+static const HotSpotActivationID kActivateTSAReadyToTransport = 2;
+static const HotSpotActivationID kActivateTSARobotsAwake = 3;
+static const HotSpotActivationID kActivateTSA0BZoomedOut = 4;
+static const HotSpotActivationID kActivateTSA0BZoomedIn = 5;
+static const HotSpotActivationID kActivateTSA0BComparisonVideo = 6;
+static const HotSpotActivationID kActivationLogReaderOpen = 7;
+static const HotSpotActivationID kActivateTSA0BTBPVideo = 8;
+static const HotSpotActivationID kActivationDoesntHaveKey = 9;
+static const HotSpotActivationID kActivationKeyVaultOpen = 10;
+static const HotSpotActivationID kActivationDoesntHaveChips = 11;
+static const HotSpotActivationID kActivationChipVaultOpen = 12;
+static const HotSpotActivationID kActivationJumpToPrehistoric = 13;
+static const HotSpotActivationID kActivationJumpToNorad = 14;
+static const HotSpotActivationID kActivationJumpToMars = 15;
+static const HotSpotActivationID kActivationJumpToWSC = 16;
+static const HotSpotActivationID kActivationReadyToExit = 17;
+static const HotSpotActivationID kActivationReadyForJumpMenu = 18;
+static const HotSpotActivationID kActivationMainJumpMenu = 19;
+
+// Hot Spot IDs.
+static const HotSpotID kTSAGTCardDropSpotID = 5000;
+static const HotSpotID kTSAGTTokyoSpotID = 5001;
+static const HotSpotID kTSAGTCaldoriaSpotID = 5002;
+static const HotSpotID kTSAGTBeachSpotID = 5003;
+static const HotSpotID kTSAGTOtherSpotID = 5004;
+static const HotSpotID kTSA02DoorSpotID = 5005;
+static const HotSpotID kTSA03EastJimenezSpotID = 5006;
+static const HotSpotID kTSA03WestCrenshawSpotID = 5007;
+static const HotSpotID kTSA04EastMatsumotoSpotID = 5008;
+static const HotSpotID kTSA04WestCastilleSpotID = 5009;
+static const HotSpotID kTSA05EastSinclairSpotID = 5010;
+static const HotSpotID kTSA05WestWhiteSpotID = 5011;
+static const HotSpotID kTSA0AEastSpotID = 5012;
+static const HotSpotID kTSA0AWastSpotID = 5013;
+static const HotSpotID kTSA0BEastMonitorSpotID = 5014;
+static const HotSpotID kTSA0BEastMonitorOutSpotID = 5015;
+static const HotSpotID kTSA0BEastCompareNoradSpotID = 5016;
+static const HotSpotID kTSA0BEastCompareMarsSpotID = 5017;
+static const HotSpotID kTSA0BEastCompareCaldoriaSpotID = 5018;
+static const HotSpotID kTSA0BEastCompareWSCSpotID = 5019;
+static const HotSpotID kTSA0BEastLeftRewindSpotID = 5020;
+static const HotSpotID kTSA0BEastLeftPlaySpotID = 5021;
+static const HotSpotID kTSA0BEastRightRewindSpotID = 5022;
+static const HotSpotID kTSA0BEastRightPlaySpotID = 5023;
+static const HotSpotID kTSA0BEastCloseVideoSpotID = 5024;
+static const HotSpotID kTSA0BNorthMonitorSpotID = 5025;
+static const HotSpotID kTSA0BNorthMonitorOutSpotID = 5026;
+static const HotSpotID kTSA0BNorthHistLogSpotID = 5027;
+static const HotSpotID kTSA0BNorthRobotsToCommandCenterSpotID = 5028;
+static const HotSpotID kTSA0BNorthRobotsToReadyRoomSpotID = 5029;
+static const HotSpotID kTSA0BNorthRobotsToFrontDoorSpotID = 5030;
+static const HotSpotID kTSA0BWestMonitorSpotID = 5031;
+static const HotSpotID kTSA0BWestMonitorOutSpotID = 5032;
+static const HotSpotID kTSA0BWestTheorySpotID = 5033;
+static const HotSpotID kTSA0BWestBackgroundSpotID = 5034;
+static const HotSpotID kTSA0BWestProcedureSpotID = 5035;
+static const HotSpotID kTSA0BWestCloseVideoSpotID = 5036;
+static const HotSpotID kTSA0BWestPlayVideoSpotID = 5037;
+static const HotSpotID kTSA0BWestRewindVideoSpotID = 5038;
+static const HotSpotID kTSA22EastMonitorSpotID = 5039;
+static const HotSpotID kTSA22EastKeySpotID = 5040;
+static const HotSpotID kTSA23WestMonitorSpotID = 5041;
+static const HotSpotID kTSA23WestChipsSpotID = 5042;
+static const HotSpotID kTSA34NorthDoorSpotID = 5043;
+static const HotSpotID kTSA37NorthJumpToPrehistoricSpotID = 5044;
+static const HotSpotID kTSA37NorthJumpToNoradSpotID = 5045;
+static const HotSpotID kTSA37NorthCancelNoradSpotID = 5046;
+static const HotSpotID kTSA37NorthJumpToMarsSpotID = 5047;
+static const HotSpotID kTSA37NorthCancelMarsSpotID = 5048;
+static const HotSpotID kTSA37NorthJumpToWSCSpotID = 5049;
+static const HotSpotID kTSA37NorthCancelWSCSpotID = 5050;
+static const HotSpotID kTSA37NorthExitSpotID = 5051;
+static const HotSpotID kTSA37NorthJumpMenuSpotID = 5052;
+static const HotSpotID kTSA37NorthNoradMenuSpotID = 5053;
+static const HotSpotID kTSA37NorthMarsMenuSpotID = 5054;
+static const HotSpotID kTSA37NorthWSCMenuSpotID = 5055;
+
+// Extra sequence IDs.
+static const ExtraID kTSATransporterArrowLoop = 0;
+static const ExtraID kTSAArriveFromCaldoria = 1;
+static const ExtraID kTSAGTOtherChoice = 2;
+static const ExtraID kTSAGTCardSwipe = 3;
+static const ExtraID kTSAGTSelectCaldoria = 4;
+static const ExtraID kTSAGTGoToCaldoria = 5;
+static const ExtraID kTSAGTSelectBeach = 6;
+static const ExtraID kTSAGTGoToBeach = 7;
+static const ExtraID kTSAGTArriveAtBeach = 8;
+static const ExtraID kTSAGTSelectTokyo = 9;
+static const ExtraID kTSAGTGoToTokyo = 10;
+static const ExtraID kTSAGTArriveAtTokyo = 11;
+static const ExtraID kTSA02NorthZoomIn = 12;
+static const ExtraID kTSA02NorthTenSecondDoor = 13;
+static const ExtraID kTSA02NorthZoomOut = 14;
+static const ExtraID kTSA02NorthDoorWithAgent3 = 15;
+static const ExtraID kTSA03JimenezZoomIn = 16;
+static const ExtraID kTSA03JimenezSpeech = 17;
+static const ExtraID kTSA03JimenezZoomOut = 18;
+static const ExtraID kTSA03CrenshawZoomIn = 19;
+static const ExtraID kTSA03CrenshawSpeech = 20;
+static const ExtraID kTSA03CrenshawZoomOut = 21;
+static const ExtraID kTSA03SouthRobotDeath = 22;
+static const ExtraID kTSA04NorthRobotGreeting = 23;
+static const ExtraID kTSA04MatsumotoZoomIn = 24;
+static const ExtraID kTSA04MatsumotoSpeech = 25;
+static const ExtraID kTSA04MatsumotoZoomOut = 26;
+static const ExtraID kTSA04CastilleZoomIn = 27;
+static const ExtraID kTSA04CastilleSpeech = 28;
+static const ExtraID kTSA04CastilleZoomOut = 29;
+static const ExtraID kTSA05SinclairZoomIn = 30;
+static const ExtraID kTSA05SinclairSpeech = 31;
+static const ExtraID kTSA05SinclairZoomOut = 32;
+static const ExtraID kTSA05WhiteZoomIn = 33;
+static const ExtraID kTSA05WhiteSpeech = 34;
+static const ExtraID kTSA05WhiteZoomOut = 35;
+static const ExtraID kTSA0AEastRobot = 36;
+static const ExtraID kTSA0AWestRobot = 37;
+static const ExtraID kTSA16NorthRobotDeath = 38;
+static const ExtraID kTSA0BEastZoomIn = 39;
+static const ExtraID kTSA0BEastZoomedView = 40;
+static const ExtraID kTSA0BEastZoomOut = 41;
+static const ExtraID kTSA0BEastTurnLeft = 42;
+static const ExtraID kTSA0BComparisonStartup = 43;
+static const ExtraID kTSA0BComparisonView0000 = 44;
+static const ExtraID kTSA0BComparisonView0002 = 45;
+static const ExtraID kTSA0BComparisonView0020 = 46;
+static const ExtraID kTSA0BComparisonView0022 = 47;
+static const ExtraID kTSA0BComparisonView0200 = 48;
+static const ExtraID kTSA0BComparisonView0202 = 49;
+static const ExtraID kTSA0BComparisonView0220 = 50;
+static const ExtraID kTSA0BComparisonView0222 = 51;
+static const ExtraID kTSA0BComparisonView2000 = 52;
+static const ExtraID kTSA0BComparisonView2002 = 53;
+static const ExtraID kTSA0BComparisonView2020 = 54;
+static const ExtraID kTSA0BComparisonView2022 = 55;
+static const ExtraID kTSA0BComparisonView2200 = 56;
+static const ExtraID kTSA0BComparisonView2202 = 57;
+static const ExtraID kTSA0BComparisonView2220 = 58;
+static const ExtraID kTSA0BComparisonView2222 = 59;
+static const ExtraID kTSA0BNoradComparisonView = 60;
+static const ExtraID kTSA0BNoradUnaltered = 61;
+static const ExtraID kTSA0BNoradAltered = 62;
+static const ExtraID kTSA0BMarsComparisonView = 63;
+static const ExtraID kTSA0BMarsUnaltered = 64;
+static const ExtraID kTSA0BMarsAltered = 65;
+static const ExtraID kTSA0BWSCComparisonView = 66;
+static const ExtraID kTSA0BWSCUnaltered = 67;
+static const ExtraID kTSA0BWSCAltered = 68;
+static const ExtraID kTSA0BCaldoriaComparisonView = 69;
+static const ExtraID kTSA0BCaldoriaUnaltered = 70;
+static const ExtraID kTSA0BCaldoriaAltered = 71;
+static const ExtraID kTSA0BNorthZoomIn = 72;
+static const ExtraID kTSA0BNorthZoomedView = 73;
+static const ExtraID kTSA0BNorthZoomOut = 74;
+static const ExtraID kTSA0BNorthTurnLeft = 75;
+static const ExtraID kTSA0BNorthTurnRight = 76;
+static const ExtraID kTSA0BNorthHistLogOpen = 77;
+static const ExtraID kTSA0BNorthHistLogClose = 78;
+static const ExtraID kTSA0BNorthHistLogCloseWithLog = 79;
+static const ExtraID kTSA0BNorthCantChangeHistory = 80;
+static const ExtraID kTSA0BNorthYoureBusted = 81;
+static const ExtraID kTSA0BNorthFinallyHappened = 82;
+static const ExtraID kTSA0BShowRip1 = 83;
+static const ExtraID kTSA0BNorthRipView1 = 84;
+static const ExtraID kTSA0BShowRip2 = 85;
+static const ExtraID kTSA0BShowGuardRobots = 86;
+static const ExtraID kTSA0BAIInterruption = 87;
+static const ExtraID kTSA0BRobotsToCommandCenter = 88;
+static const ExtraID kTSA0BNorthRobotsAtCCView = 89;
+static const ExtraID kTSA0BNorthRobotsAtRRView = 90;
+static const ExtraID kTSA0BNorthRobotsAtFDView = 91;
+static const ExtraID kTSA0BRobotsFromCommandCenterToReadyRoom = 92;
+static const ExtraID kTSA0BRobotsFromReadyRoomToCommandCenter = 93;
+static const ExtraID kTSA0BRobotsFromCommandCenterToFrontDoor = 94;
+static const ExtraID kTSA0BRobotsFromFrontDoorToCommandCenter = 95;
+static const ExtraID kTSA0BRobotsFromFrontDoorToReadyRoom = 96;
+static const ExtraID kTSA0BRobotsFromReadyRoomToFrontDoor = 97;
+static const ExtraID kTSA0BWestZoomIn = 98;
+static const ExtraID kTSA0BWestZoomedView = 99;
+static const ExtraID kTSA0BWestZoomOut = 100;
+static const ExtraID kTSA0BWestTurnRight = 101;
+static const ExtraID kTSA0BTBPTheoryHighlight = 102;
+static const ExtraID kTSA0BTBPBackgroundHighlight = 103;
+static const ExtraID kTSA0BTBPProcedureHighlight = 104;
+static const ExtraID kTSA0BTBPTheory = 105;
+static const ExtraID kTSA0BTBPBackground = 106;
+static const ExtraID kTSA0BTBPProcedure = 107;
+static const ExtraID kTSA0BRipAlarmScreen = 108;
+static const ExtraID kTSA22RedEastZoomInSequence = 109;
+static const ExtraID kTSA22RedEastVaultViewWithKey = 110;
+static const ExtraID kTSA22RedEastVaultViewNoKey = 111;
+static const ExtraID kTSA23RedWestVaultZoomInSequence = 112;
+static const ExtraID kTSA23RedWestVaultViewWithChips = 113;
+static const ExtraID kTSA23RedWestVaultViewNoChips = 114;
+static const ExtraID kTSA25NorthDeniedNoKey = 115;
+static const ExtraID kTSA25NorthDeniedNoChip = 116;
+static const ExtraID kTSA25NorthPutOnSuit = 117;
+static const ExtraID kTSA25NorthAlreadyHaveSuit = 118;
+static const ExtraID kTSA25NorthDescending1 = 119;
+static const ExtraID kTSA25NorthDescending2 = 120;
+static const ExtraID kTSA37HorseToAI1 = 121;
+static const ExtraID kTSA37PegasusAI1 = 122;
+static const ExtraID kTSA37AI1ToCommissioner1 = 123;
+static const ExtraID kTSA37Commissioner1 = 124;
+static const ExtraID kTSA37Commissioner1ToZoom = 125;
+static const ExtraID kTSA37ZoomToPrehistoric = 126;
+static const ExtraID kTSA37PrehistoricToAI2 = 127;
+static const ExtraID kTSA37PegasusAI2 = 128;
+static const ExtraID kTSA37AI2ToPrehistoric = 129;
+static const ExtraID kTSA37PrehistoricToDepart = 130;
+static const ExtraID kTSA37PegasusDepart = 131;
+static const ExtraID kTSA37TimeJumpToPegasus = 132;
+static const ExtraID kTSA37RecallToDownload = 133;
+static const ExtraID kTSA37DownloadToColonel1 = 134;
+static const ExtraID kTSA37Colonel1 = 135;
+static const ExtraID kTSA37Colonel1ToReviewRequired = 136;
+static const ExtraID kTSA37ReviewRequiredToExit = 137;
+static const ExtraID kTSA37ExitHilited = 138;
+static const ExtraID kTSA37ExitToHorse = 139;
+static const ExtraID kTSA37HorseToColonel2 = 140;
+static const ExtraID kTSA37Colonel2 = 141;
+static const ExtraID kTSA37PegasusAI3 = 142;
+static const ExtraID kTSA37AI3ToHorse = 143;
+static const ExtraID kTSA37HorseToZoom = 144;
+static const ExtraID kTSA37ZoomToMainMenu = 145;
+static const ExtraID kTSA37MainMenuToAI4 = 146;
+static const ExtraID kTSA37PegasusAI4 = 147;
+static const ExtraID kTSA37AI4ToMainMenu = 148;
+static const ExtraID kTSA37JumpMenu000 = 149;
+static const ExtraID kTSA37JumpMenu001 = 150;
+static const ExtraID kTSA37JumpMenu010 = 151;
+static const ExtraID kTSA37JumpMenu011 = 152;
+static const ExtraID kTSA37JumpMenu100 = 153;
+static const ExtraID kTSA37JumpMenu101 = 154;
+static const ExtraID kTSA37JumpMenu110 = 155;
+static const ExtraID kTSA37JumpMenu111 = 156;
+static const ExtraID kTSA37JumpToWSCMenu = 157;
+static const ExtraID kTSA37CancelWSC = 158;
+static const ExtraID kTSA37JumpToWSC = 159;
+static const ExtraID kTSA37WSCToAI5 = 160;
+static const ExtraID kTSA37PegasusAI5 = 161;
+static const ExtraID kTSA37AI5ToWSC = 162;
+static const ExtraID kTSA37WSCToDepart = 163;
+static const ExtraID kTSA37JumpToMarsMenu = 164;
+static const ExtraID kTSA37CancelMars = 165;
+static const ExtraID kTSA37JumpToMars = 166;
+static const ExtraID kTSA37MarsToAI6 = 167;
+static const ExtraID kTSA37PegasusAI6 = 168;
+static const ExtraID kTSA37AI6ToMars = 169;
+static const ExtraID kTSA37MarsToDepart = 170;
+static const ExtraID kTSA37JumpToNoradMenu = 171;
+static const ExtraID kTSA37CancelNorad = 172;
+static const ExtraID kTSA37JumpToNorad = 173;
+static const ExtraID kTSA37NoradToAI7 = 174;
+static const ExtraID kTSA37PegasusAI7 = 175;
+static const ExtraID kTSA37AI7ToNorad = 176;
+static const ExtraID kTSA37NoradToDepart = 177;
+static const ExtraID kTSA37EnvironmentalScan = 178;
+static const ExtraID kTSA37DownloadToMainMenu = 179;
+static const ExtraID kTSA37DownloadToOpMemReview = 180;
+static const ExtraID kTSA37OpMemReviewToMainMenu = 181;
+static const ExtraID kTSA37OpMemReviewToAllClear = 182;
+static const ExtraID kTSA37AllClearToCongratulations = 183;
+static const ExtraID kTSA37Congratulations = 184;
+static const ExtraID kTSA37CongratulationsToExit = 185;
+
+const DisplayOrder kRipTimerOrder = kMonitorLayer;
+
+
+const CoordType kUnresolvedLeft = kNavAreaLeft + 14;
+const CoordType kUnresolvedTop = kNavAreaTop + 236;
+
+const CoordType kResolvedLeft = kNavAreaLeft + 36;
+const CoordType kResolvedTop = kNavAreaTop + 236;
+
+const CoordType kJumpMenuLeft = kNavAreaLeft + 360;
+const CoordType kJumpMenuTop = kNavAreaTop + 202;
+
+const CoordType kJumpMenuHilitedLeft = kNavAreaLeft + 354;
+const CoordType kJumpMenuHilitedTop = kNavAreaTop + 196;
+
+const CoordType kExitLeft = kNavAreaLeft + 360;
+const CoordType kExitTop = kNavAreaTop + 216;
+
+const CoordType kExitHilitedLeft = kNavAreaLeft + 354;
+const CoordType kExitHilitedTop = kNavAreaTop + 210;
+
+const CoordType kRipTimerLeft = kNavAreaLeft + 95;
+const CoordType kRipTimerTop = kNavAreaTop + 87;
+
+const CoordType kTBPCloseLeft = kNavAreaLeft + 30;
+const CoordType kTBPCloseTop = kNavAreaTop + 16;
+
+const CoordType kTBPRewindLeft = kNavAreaLeft + 86;
+const CoordType kTBPRewindTop = kNavAreaTop + 218;
+
+const CoordType kComparisonCloseLeft = kNavAreaLeft + 50;
+const CoordType kComparisonCloseTop = kNavAreaTop + 14;
+
+const CoordType kComparisonLeftRewindLeft = kNavAreaLeft + 96;
+const CoordType kComparisonLeftRewindTop = kNavAreaTop + 190;
+
+const CoordType kComparisonRightRewindLeft = kNavAreaLeft + 282;
+const CoordType kComparisonRightRewindTop = kNavAreaTop + 190;
+
+const CoordType kComparisonHiliteSpriteLeft = kNavAreaLeft + 45;
+const CoordType kComparisonHiliteSpriteTop = kNavAreaTop + 65;
+
+const CoordType kComparisonHiliteNoradLeft = kNavAreaLeft + 45;
+const CoordType kComparisonHiliteNoradTop = kNavAreaTop + 65;
+
+const CoordType kComparisonHiliteMarsLeft = kNavAreaLeft + 45 + 4;
+const CoordType kComparisonHiliteMarsTop = kNavAreaTop + 65 + 23;
+
+const CoordType kComparisonHiliteCaldoriaLeft = kNavAreaLeft + 45 + 7;
+const CoordType kComparisonHiliteCaldoriaTop = kNavAreaTop + 65 + 46;
+
+const CoordType kComparisonHiliteWSCLeft = kNavAreaLeft + 45 + 11;
+const CoordType kComparisonHiliteWSCTop = kNavAreaTop + 65 + 68;
+
+const CoordType kComparisonChancesSpriteLeft = kNavAreaLeft + 148;
+const CoordType kComparisonChancesSpriteTop = kNavAreaTop + 162;
+
+const CoordType kComparisonChancesNoradLeft = kNavAreaLeft + 148;
+const CoordType kComparisonChancesNoradTop = kNavAreaTop + 162;
+
+const CoordType kComparisonChancesMarsLeft = kNavAreaLeft + 148;
+const CoordType kComparisonChancesMarsTop = kNavAreaTop + 162;
+
+const CoordType kComparisonChancesCaldoriaLeft = kNavAreaLeft + 148;
+const CoordType kComparisonChancesCaldoriaTop = kNavAreaTop + 162 + 1;
+
+const CoordType kComparisonChancesWSCLeft = kNavAreaLeft + 148;
+const CoordType kComparisonChancesWSCTop = kNavAreaTop + 162;
+
+const CoordType kRedirectionSprite1Left = kNavAreaLeft + 58;
+const CoordType kRedirectionSprite1Top = kNavAreaTop + 16;
+
+const CoordType kRedirectionSprite2Left = kNavAreaLeft + 36;
+const CoordType kRedirectionSprite2Top = kNavAreaTop + 166;
+
+const CoordType kRedirectionCCRolloverLeft = kNavAreaLeft + 58;
+const CoordType kRedirectionCCRolloverTop = kNavAreaTop + 16;
+
+const CoordType kRedirectionRRRolloverLeft = kNavAreaLeft + 430;
+const CoordType kRedirectionRRRolloverTop = kNavAreaTop + 30;
+
+const CoordType kRedirectionFDRolloverLeft = kNavAreaLeft + 278;
+const CoordType kRedirectionFDRolloverTop = kNavAreaTop + 160;
+
+const CoordType kRedirectionCCDoorLeft = kNavAreaLeft + 174;
+const CoordType kRedirectionCCDoorTop = kNavAreaTop + 36;
+
+const CoordType kRedirectionRRDoorLeft = kNavAreaLeft + 418;
+const CoordType kRedirectionRRDoorTop = kNavAreaTop + 32;
+
+const CoordType kRedirectionFDDoorLeft = kNavAreaLeft + 298;
+const CoordType kRedirectionFDDoorTop = kNavAreaTop + 240;
+
+const CoordType kRedirectionSecuredLeft = kNavAreaLeft + 36;
+const CoordType kRedirectionSecuredTop = kNavAreaTop + 166;
+
+const CoordType kRedirectionNewTargetLeft = kNavAreaLeft + 36;
+const CoordType kRedirectionNewTargetTop = kNavAreaTop + 166;
+
+const CoordType kRedirectionCloseLeft = kNavAreaLeft + 56;
+const CoordType kRedirectionCloseTop = kNavAreaTop + 220;
+
+static const TimeValue kTSABumpIntoWallIn = 0;
+static const TimeValue kTSABumpIntoWallOut = 148;
+
+static const TimeValue kTSAGTDoorCloseIn = 148;
+static const TimeValue kTSAGTDoorCloseOut = 1570;
+
+static const TimeValue kTSANoOtherDestinationIn = 1570;
+static const TimeValue kTSANoOtherDestinationOut = 3601;
+
+static const TimeValue kTSAEntryDoorCloseIn = 3601;
+static const TimeValue kTSAEntryDoorCloseOut = 4200;
+
+static const TimeValue kTSAInsideDoorCloseIn = 4200;
+static const TimeValue kTSAInsideDoorCloseOut = 4800;
+
+static const TimeValue kTSAVaultCloseIn = 4800;
+static const TimeValue kTSAVaultCloseOut = 5388;
+
+static const TimeValue kTSAPegasusDoorCloseIn = 5388;
+static const TimeValue kTSAPegasusDoorCloseOut = 6457;
+
+static const bool kPegasusUnresolved = false;
+static const bool kPegasusResolved = true;
+static const bool kPegasusCantExit = false;
+static const bool kPegasusCanExit = true;
+
+// Monitor modes
+enum {
+ kMonitorNeutral = 0,
+ kMonitorTheory = 1,
+ kMonitorProcedure = 2,
+ kMonitorBackground = 3,
+ kMonitorNoradComparison = 4,
+ kMonitorMarsComparison = 5,
+ kMonitorCaldoriaComparison = 6,
+ kMonitorWSCComparison = 7,
+
+ kRawModeMask = 0x0F,
+ kPlayingTBPMask = 0x10,
+ kPlayingLeftComparisonMask = 0x20,
+ kPlayingRightComparisonMask = 0x40,
+
+ kPlayingAnyMask = kPlayingTBPMask |
+ kPlayingLeftComparisonMask |
+ kPlayingRightComparisonMask,
+
+ kMonitorPlayingTheory = kMonitorTheory | kPlayingTBPMask,
+ kMonitorPlayingProcedure = kMonitorProcedure | kPlayingTBPMask,
+ kMonitorPlayingBackground = kMonitorBackground | kPlayingTBPMask,
+
+ kMonitorPlayingLeftNoradComparison = kMonitorNoradComparison |
+ kPlayingLeftComparisonMask,
+ kMonitorPlayingRightNoradComparison = kMonitorNoradComparison |
+ kPlayingRightComparisonMask,
+ kMonitorPlayingLeftMarsComparison = kMonitorMarsComparison |
+ kPlayingLeftComparisonMask,
+ kMonitorPlayingRightMarsComparison = kMonitorMarsComparison |
+ kPlayingRightComparisonMask,
+ kMonitorPlayingLeftCaldoriaComparison = kMonitorCaldoriaComparison |
+ kPlayingLeftComparisonMask,
+ kMonitorPlayingRightCaldoriaComparison = kMonitorCaldoriaComparison |
+ kPlayingRightComparisonMask,
+ kMonitorPlayingLeftWSCComparison = kMonitorWSCComparison |
+ kPlayingLeftComparisonMask,
+ kMonitorPlayingRightWSCComparison = kMonitorWSCComparison |
+ kPlayingRightComparisonMask
+};
+
+static const ExtraID s_historicalLogViews[16] = {
+ kTSA0BComparisonView0000,
+ kTSA0BComparisonView0002,
+ kTSA0BComparisonView0020,
+ kTSA0BComparisonView0022,
+ kTSA0BComparisonView0200,
+ kTSA0BComparisonView0202,
+ kTSA0BComparisonView0220,
+ kTSA0BComparisonView0222,
+ kTSA0BComparisonView2000,
+ kTSA0BComparisonView2002,
+ kTSA0BComparisonView2020,
+ kTSA0BComparisonView2022,
+ kTSA0BComparisonView2200,
+ kTSA0BComparisonView2202,
+ kTSA0BComparisonView2220,
+ kTSA0BComparisonView2222
+};
+
+static const int kRedirectionCCRolloverSprite = 0;
+static const int kRedirectionRRRolloverSprite = 1;
+static const int kRedirectionFDRolloverSprite = 2;
+static const int kRedirectionCCDoorSprite = 3;
+static const int kRedirectionRRDoorSprite = 4;
+static const int kRedirectionFDDoorSprite = 5;
+static const int kRedirectionCloseSprite = 6;
+static const int kRedirectionSecuredSprite = 0;
+static const int kRedirectionNewTargetSprite = 1;
+
+void RipTimer::initImage() {
+ _middle = -1;
+
+ _timerImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLeftRipPICTID);
+
+ Common::Rect r;
+ _timerImage.getSurfaceBounds(r);
+ setBounds(r);
+}
+
+void RipTimer::releaseImage() {
+ _timerImage.deallocateSurface();
+}
+
+void RipTimer::draw(const Common::Rect &updateRect) {
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ Common::Rect r1 = bounds;
+ r1.right = _middle;
+ r1 = updateRect.findIntersectingRect(r1);
+
+ if (!r1.isEmpty()) {
+ Common::Rect r2 = r1;
+ r2.moveTo(r1.left - _bounds.left, r1.top - bounds.top);
+ _timerImage.copyToCurrentPort(r2, r1);
+ }
+}
+
+void RipTimer::timeChanged(const TimeValue newTime) {
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ CoordType newMiddle = bounds.left + bounds.width() * newTime / getDuration();
+
+ if (newMiddle != _middle) {
+ _middle = newMiddle;
+ triggerRedraw();
+ }
+
+ if (newTime == getStop())
+ ((PegasusEngine *)g_engine)->die(kDeathUncreatedInTSA);
+}
+
+FullTSA::FullTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Full TSA", kFullTSAID),
+ _ripTimer(kNoDisplayElement), _sprite1(kNoDisplayElement), _sprite2(kNoDisplayElement), _sprite3(kNoDisplayElement) {
+ setIsItemTaken(kJourneymanKey);
+ setIsItemTaken(kPegasusBiochip);
+ setIsItemTaken(kMapBiochip);
+}
+
+void FullTSA::init() {
+ Neighborhood::init();
+ _ripTimer.setDisplayOrder(kRipTimerOrder);
+ _ripTimer.startDisplaying();
+
+ if (!GameState.getTSASeenRobotGreeting())
+ forceStridingStop(kTSA03, kNorth, kNoAlternateID);
+
+ _sprite1.setDisplayOrder(kMonitorLayer);
+ _sprite1.startDisplaying();
+ _sprite2.setDisplayOrder(kMonitorLayer);
+ _sprite2.startDisplaying();
+ _sprite3.setDisplayOrder(kMonitorLayer);
+ _sprite3.startDisplaying();
+
+ // Fix a mistake in the world builder tables.
+ HotspotInfoTable::Entry *entry = findHotspotEntry(kTSA23WestChipsSpotID);
+ entry->hotspotItem = kPegasusBiochip;
+}
+
+void FullTSA::dieUncreatedInTSA() {
+ die(kDeathUncreatedInTSA);
+}
+
+void FullTSA::start() {
+ g_energyMonitor->stopEnergyDraining();
+
+ if (!GameState.getScoringEnterTSA()) {
+ _utilityFuse.primeFuse(GameState.getTSAFuseTimeLimit());
+ _utilityFuse.setFunctor(new Common::Functor0Mem<void, FullTSA>(this, &FullTSA::dieUncreatedInTSA));
+ _utilityFuse.lightFuse();
+ } else if (GameState.getTSAState() == kTSAPlayerDetectedRip || GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) {
+ _ripTimer.initImage();
+ _ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop);
+ _ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale);
+ _ripTimer.setTime(GameState.getRipTimerTime());
+ _ripTimer.start();
+ }
+
+ Neighborhood::start();
+}
+
+void FullTSA::flushGameState() {
+ GameState.setRipTimerTime(_ripTimer.getTime());
+ GameState.setTSAFuseTimeLimit(_utilityFuse.getTimeRemaining());
+}
+
+Common::String FullTSA::getBriefingMovie() {
+ Common::String movieName = Neighborhood::getBriefingMovie();
+
+ if (movieName.empty()) {
+ RoomID room = GameState.getCurrentRoom();
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerNotArrived:
+ case kTSAPlayerForcedReview:
+ if (room >= kTSA16 && room <= kTSA0B)
+ return "Images/AI/TSA/XT01A";
+
+ return "Images/AI/TSA/XT01";
+ case kTSAPlayerDetectedRip:
+ case kTSAPlayerNeedsHistoricalLog:
+ return "Images/AI/TSA/XT02";
+ case kTSAPlayerGotHistoricalLog:
+ case kTSAPlayerInstalledHistoricalLog:
+ return "Images/AI/TSA/XT03";
+ default:
+ switch (getCurrentActivation()) {
+ case kActivationJumpToPrehistoric:
+ g_AIChip->showBriefingClicked();
+ startExtraSequenceSync(kTSA37PegasusAI2, kHintInterruption);
+ startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput);
+ g_AIChip->clearClicked();
+ break;
+ case kActivationJumpToNorad:
+ g_AIChip->showBriefingClicked();
+ startExtraSequenceSync(kTSA37PegasusAI7, kHintInterruption);
+ startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput);
+ g_AIChip->clearClicked();
+ break;
+ case kActivationJumpToMars:
+ g_AIChip->showBriefingClicked();
+ startExtraSequenceSync(kTSA37PegasusAI6, kHintInterruption);
+ startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput);
+ g_AIChip->clearClicked();
+ break;
+ case kActivationJumpToWSC:
+ g_AIChip->showBriefingClicked();
+ startExtraSequenceSync(kTSA37PegasusAI5, kHintInterruption);
+ startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput);
+ g_AIChip->clearClicked();
+ break;
+ default:
+ if (GameState.allTimeZonesFinished())
+ return "Images/AI/TSA/XT05";
+
+ return "Images/AI/TSA/XT04";
+ }
+ break;
+ }
+ }
+
+ return movieName;
+}
+
+Common::String FullTSA::getEnvScanMovie() {
+ Common::String movieName = Neighborhood::getEnvScanMovie();
+
+ if (movieName.empty()) {
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerNotArrived:
+ case kTSAPlayerForcedReview:
+ case kTSAPlayerDetectedRip:
+ case kTSAPlayerNeedsHistoricalLog:
+ return "Images/AI/TSA/XTE1";
+ default:
+ if (GameState.getCurrentRoom() == kTSA37) {
+ g_AIChip->showEnvScanClicked();
+ startExtraSequenceSync(kTSA37EnvironmentalScan, kHintInterruption);
+
+ switch (getCurrentActivation()) {
+ case kActivationJumpToPrehistoric:
+ startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput);
+ break;
+ case kActivationJumpToNorad:
+ startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput);
+ showExtraView(kTSA37JumpToNoradMenu);
+ break;
+ case kActivationJumpToMars:
+ startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput);
+ showExtraView(kTSA37JumpToMarsMenu);
+ break;
+ case kActivationJumpToWSC:
+ startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput);
+ showExtraView(kTSA37JumpToWSCMenu);
+ break;
+ default:
+ startExtraSequenceSync(kTSA37AI4ToMainMenu, kFilterNoInput);
+ break;
+ }
+
+ g_AIChip->clearClicked();
+ } else if (GameState.allTimeZonesFinished()) {
+ return "Images/AI/TSA/XTE1";
+ } else {
+ return "Images/AI/TSA/XTE2";
+ }
+ break;
+ }
+ }
+
+ return movieName;
+}
+
+uint FullTSA::getNumHints() {
+ uint numHints = Neighborhood::getNumHints();
+
+ if (numHints == 0) {
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ if (GameState.getCurrentRoom() == kTSA0B && GameState.getTSA0BZoomedIn())
+ numHints = 3;
+ break;
+ }
+ }
+
+ return numHints;
+}
+
+Common::String FullTSA::getHintMovie(uint hintNum) {
+ Common::String movieName = Neighborhood::getHintMovie(hintNum);
+
+ if (movieName.empty())
+ movieName = Common::String::format("Images/AI/TSA/XT20NH%d", hintNum);
+
+ return movieName;
+}
+
+void FullTSA::loadAmbientLoops() {
+ RoomID room = GameState.getCurrentRoom();
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerDetectedRip:
+ case kTSAPlayerNeedsHistoricalLog:
+ if ((room >= kTSA16 && room <= kTSA0B) || (room >= kTSA21Cyan && room <= kTSA24Cyan) || (room >= kTSA21Red && room <= kTSA24Red))
+ loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 4, 0, 0);
+ else if (room == kTSA25Cyan || room == kTSA25Red)
+ loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 6, 0, 0);
+ else
+ loadLoopSound1("Sounds/TSA/TSA EchoClaxon.22K.AIFF", 0x100 / 4, 0, 0);
+ break;
+ default:
+ if (room >= kTSA00 && room <= kTSA02)
+ loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
+ else if (room >= kTSA03 && room <= kTSA15)
+ loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
+ else if (room >= kTSA16 && room <= kTSA0B)
+ loadLoopSound1("Sounds/TSA/T14SAEO1.22K.AIFF");
+ else if (room >= kTSA21Cyan && room <= kTSA25Red)
+ loadLoopSound1("Sounds/TSA/T15SAE01.22K.AIFF");
+ else if (room >= kTSA26 && room <= kTSA37)
+ loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
+ break;
+ }
+}
+
+short FullTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
+ int16 result = Neighborhood::getStaticCompassAngle(room, dir);
+
+ switch (room) {
+ case kTSA08:
+ result += kCompassShift;
+ break;
+ case kTSA09:
+ result -= kCompassShift;
+ break;
+ case kTSA10:
+ result += kCompassShift * 2;
+ break;
+ case kTSA11:
+ case kTSA22Cyan:
+ case kTSA22Red:
+ result -= kCompassShift * 2;
+ break;
+ case kTSA12:
+ result += kCompassShift * 3;
+ break;
+ case kTSA13:
+ result -= kCompassShift * 3;
+ break;
+ case kTSA14:
+ case kTSA16:
+ case kTSA17:
+ case kTSA18:
+ case kTSA19:
+ result += kCompassShift * 4;
+ break;
+ case kTSA0B:
+ result += kCompassShift * 4;
+
+ if (dir == kWest)
+ result += 30;
+ else if (dir == kEast)
+ result -= 30;
+ break;
+ case kTSA33:
+ result += kCompassShift * 4;
+ break;
+ case kTSA15:
+ case kTSA21Cyan:
+ case kTSA24Cyan:
+ case kTSA25Cyan:
+ case kTSA21Red:
+ case kTSA24Red:
+ case kTSA25Red:
+ case kTSA26:
+ case kTSA27:
+ case kTSA28:
+ case kTSA29:
+ case kTSA30:
+ result -= kCompassShift * 4;
+ break;
+ case kTSA23Cyan:
+ case kTSA23Red:
+ result -= kCompassShift * 6;
+ break;
+ case kTSA32:
+ result -= kCompassShift * 8;
+ break;
+ case kTSA34:
+ result -= kCompassShift * 12;
+ break;
+ case kTSA35:
+ result += kCompassShift * 8;
+ break;
+ case kTSA37:
+ result -= kCompassShift * 2;
+ break;
+ }
+
+ return result;
+}
+
+void FullTSA::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) {
+ Neighborhood::getExitCompassMove(exitEntry, compassMove);
+
+ switch (MakeRoomView(exitEntry.room, exitEntry.direction)) {
+ case MakeRoomView(kTSA01, kSouth):
+ compassMove.insertFaderKnot(exitEntry.movieStart, -180);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 3, -180);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 33,
+ getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection));
+ break;
+ case MakeRoomView(kTSA11, kEast):
+ if (getCurrentAlternate() == kAltTSARobotsAtReadyRoom) {
+ compassMove.makeTwoKnotFaderSpec(kFullTSAMovieScale, exitEntry.movieStart,
+ getStaticCompassAngle(kTSA11, kEast), exitEntry.movieEnd,
+ getStaticCompassAngle(kTSA13, kEast));
+ compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 13, compassMove.getNthKnotValue(1));
+ }
+ break;
+ case MakeRoomView(kTSA34, kNorth):
+ compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 48,
+ getStaticCompassAngle(exitEntry.room, exitEntry.direction));
+ compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 68,
+ getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection));
+ break;
+ case MakeRoomView(kTSA37, kNorth):
+ compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 38,
+ getStaticCompassAngle(exitEntry.room, exitEntry.direction));
+ compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 64,
+ getStaticCompassAngle(exitEntry.room, exitEntry.direction) + kCompassShift * 3 / 2);
+ compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 105,
+ getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection));
+ break;
+ }
+}
+
+void FullTSA::getExtraCompassMove(const ExtraTable::Entry &extraEntry, FaderMoveSpec &compassMove) {
+ int16 angle;
+
+ switch (extraEntry.extra) {
+ case kTSA0BEastTurnLeft:
+ case kTSA0BNorthTurnLeft:
+ angle =getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle,
+ extraEntry.movieEnd, angle - 60);
+ break;
+ case kTSA0BNorthTurnRight:
+ case kTSA0BWestTurnRight:
+ angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle,
+ extraEntry.movieEnd, angle + 60);
+ break;
+ case kTSA22RedEastZoomInSequence:
+ angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle,
+ extraEntry.movieEnd, angle);
+ compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2);
+ compassMove.insertFaderKnot(extraEntry.movieStart + 8160, angle - kCompassShift * 2);
+ compassMove.insertFaderKnot(extraEntry.movieStart + 9840, angle);
+ break;
+ case kTSA23RedWestVaultZoomInSequence:
+ angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle,
+ extraEntry.movieEnd, angle);
+ compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2);
+ compassMove.insertFaderKnot(extraEntry.movieStart + 10100, angle - kCompassShift * 2);
+ compassMove.insertFaderKnot(extraEntry.movieStart + 11880, angle);
+ break;
+ default:
+ Neighborhood::getExtraCompassMove(extraEntry, compassMove);
+ break;
+ }
+}
+
+uint16 FullTSA::getDateResID() const {
+ return kDate2318ID;
+}
+
+TimeValue FullTSA::getViewTime(const RoomID room, const DirectionConstant direction) {
+ ExtraID extraID = 0xffffffff;
+
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kTSA0B, kEast):
+ if (GameState.getTSA0BZoomedIn())
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerInstalledHistoricalLog:
+ case kTSABossSawHistoricalLog:
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ extraID = s_historicalLogViews[getHistoricalLogIndex()];
+ break;
+ default:
+ extraID = kTSA0BEastZoomedView;
+ break;
+ }
+ break;
+ case MakeRoomView(kTSA0B, kNorth):
+ if (GameState.getTSA0BZoomedIn())
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerNeedsHistoricalLog:
+ extraID = kTSA0BNorthRipView1;
+ break;
+ default:
+ extraID = kTSA0BNorthZoomedView;
+ break;
+ }
+ break;
+ case MakeRoomView(kTSA0B, kWest):
+ if (GameState.getTSA0BZoomedIn())
+ extraID = kTSA0BWestZoomedView;
+ break;
+ case MakeRoomView(kTSA22Red, kEast):
+ if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) {
+ if (_vm->itemInLocation(kJourneymanKey, kFullTSAID, kTSA22Red, kEast))
+ extraID = kTSA22RedEastVaultViewWithKey;
+ else
+ extraID = kTSA22RedEastVaultViewNoKey;
+ }
+ break;
+ case MakeRoomView(kTSA23Red, kWest):
+ if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) {
+ if (_vm->itemInLocation(kPegasusBiochip, kFullTSAID, kTSA23Red, kWest))
+ extraID = kTSA23RedWestVaultViewWithChips;
+ else
+ extraID = kTSA23RedWestVaultViewNoChips;
+ }
+ break;
+ case MakeRoomView(kTSA37, kNorth):
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerGotHistoricalLog:
+ extraID = kTSA37ReviewRequiredToExit;
+ break;
+ case kPlayerFinishedWithTSA:
+ extraID = kTSA37CongratulationsToExit;
+ break;
+ default:
+ extraID = kTSA37AI3ToHorse;
+ break;
+ }
+ break;
+ }
+
+ if (extraID != 0xffffffff) {
+ ExtraTable::Entry entry;
+ getExtraEntry(extraID, entry);
+ return entry.movieEnd - 1;
+ }
+
+ return Neighborhood::getViewTime(room, direction);
+}
+
+void FullTSA::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kTSA0B, kNorth):
+ case MakeRoomView(kTSA0B, kEast):
+ case MakeRoomView(kTSA0B, kWest):
+ if (!GameState.getTSA0BZoomedIn())
+ Neighborhood::findSpotEntry(room, direction, flags, entry);
+ break;
+ default:
+ Neighborhood::findSpotEntry(room, direction, flags, entry);
+ break;
+ }
+}
+
+void FullTSA::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) {
+ Neighborhood::getExtraEntry(id, extraEntry);
+
+ if (id == kTSA0BShowGuardRobots)
+ extraEntry.movieStart += kFullTSAFrameDuration * 3;
+}
+
+void FullTSA::pickedUpItem(Item *item) {
+ BiochipItem *biochip;
+
+ switch (item->getObjectID()) {
+ case kJourneymanKey:
+ GameState.setScoringGotJourneymanKey(true);
+ break;
+ case kPegasusBiochip:
+ biochip = (BiochipItem *)_vm->getAllItems().findItemByID(kMapBiochip);
+ _vm->addItemToBiochips(biochip);
+ GameState.setScoringGotPegasusBiochip(true);
+ break;
+ }
+}
+
+void FullTSA::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) {
+ switch (extraEntry.extra) {
+ case kTSA0BNorthZoomIn:
+ if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) {
+ _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false);
+ requestExtraSequence(kTSA0BNorthHistLogClose, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput);
+ }
+ break;
+ case kTSA0BNorthZoomOut:
+ if (_ripTimer.isVisible())
+ _ripTimer.hide();
+
+ shutDownRobotMonitor();
+ Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput);
+ break;
+ case kTSA0BEastZoomOut:
+ shutDownComparisonMonitor();
+ Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput);
+ break;
+ default:
+ Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput);
+ break;
+ }
+}
+
+void FullTSA::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kTSA00, kNorth):
+ if (GameState.getLastNeighborhood() != kFullTSAID) {
+ startExtraSequence(kTSAArriveFromCaldoria, kDoorOpenCompletedFlag, kFilterNoInput);
+ return;
+ }
+ break;
+ case MakeRoomView(kTSA02, kNorth):
+ if (!GameState.getTSAIDedAtDoor()) {
+ GameState.setTSAIDedAtDoor(true);
+ requestExtraSequence(kTSA02NorthZoomIn, 0, kFilterNoInput);
+ requestExtraSequence(kTSA02NorthTenSecondDoor, 0, kFilterNoInput);
+
+ if (GameState.getTSASeenAgent3AtDoor()) {
+ requestExtraSequence(kTSA02NorthZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ GameState.setTSASeenAgent3AtDoor(true);
+ requestExtraSequence(kTSA02NorthZoomOut, 0, kFilterNoInput);
+ requestExtraSequence(kTSA02NorthDoorWithAgent3, kDoorOpenCompletedFlag, kFilterNoInput);
+ }
+ return;
+ }
+ break;
+ case MakeRoomView(kTSA03, kSouth):
+ if (GameState.getTSAState() == kRobotsAtFrontDoor) {
+ playDeathExtra(kTSA03SouthRobotDeath, kDeathShotByTSARobots);
+ return;
+ }
+ break;
+ case MakeRoomView(kTSA16, kNorth):
+ if (GameState.getTSAState() == kRobotsAtCommandCenter) {
+ playDeathExtra(kTSA16NorthRobotDeath, kDeathShotByTSARobots);
+ return;
+ }
+ break;
+ }
+
+ Neighborhood::startDoorOpenMovie(startTime, stopTime);
+}
+
+InputBits FullTSA::getInputFilter() {
+ InputBits result = Neighborhood::getInputFilter();
+
+ switch (GameState.getCurrentRoom()) {
+ case kTSA0B:
+ if (GameState.getT0BMonitorMode() != kMonitorNeutral)
+ // Only allow a click.
+ result &= JMPPPInput::getClickInputFilter();
+ break;
+ case kTSA37:
+ // Can't move forward in Pegasus. Only press the exit button.
+ result &= ~(kFilterUpButton | kFilterUpAuto);
+ break;
+ }
+
+ return result;
+}
+
+void FullTSA::turnLeft() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kTSA15, kNorth):
+ if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog)
+ setCurrentAlternate(kAltTSANormal);
+ break;
+ case MakeRoomView(kTSA0B, kNorth):
+ if (_ripTimer.isVisible())
+ _ripTimer.hide();
+ releaseSprites();
+ break;
+ case MakeRoomView(kTSA0B, kEast):
+ shutDownComparisonMonitor();
+ break;
+ }
+
+ Neighborhood::turnLeft();
+}
+
+void FullTSA::turnRight() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kTSA15, kSouth):
+ if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog)
+ setCurrentAlternate(kAltTSANormal);
+ break;
+ case MakeRoomView(kTSA0B, kNorth):
+ if (_ripTimer.isVisible())
+ _ripTimer.hide();
+ releaseSprites();
+ break;
+ case MakeRoomView(kTSA0B, kEast):
+ shutDownComparisonMonitor();
+ break;
+ }
+
+ Neighborhood::turnRight();
+}
+
+void FullTSA::openDoor() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kTSA15, kSouth):
+ if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog || GameState.getTSAState() == kRobotsAtFrontDoor)
+ setCurrentAlternate(kAltTSARedAlert);
+ break;
+ }
+
+ Neighborhood::openDoor();
+}
+
+CanMoveForwardReason FullTSA::canMoveForward(ExitTable::Entry &entry) {
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kTSA25Red, kNorth))
+ return kCantMoveBlocked;
+
+ return Neighborhood::canMoveForward(entry);
+}
+
+CanOpenDoorReason FullTSA::canOpenDoor(DoorTable::Entry &entry) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kTSA02, kNorth):
+ if (!GameState.getTSAFrontDoorUnlockedOutside())
+ return kCantOpenLocked;
+ break;
+ case MakeRoomView(kTSA03, kSouth):
+ if (!GameState.getTSAFrontDoorUnlockedInside())
+ return kCantOpenLocked;
+ break;
+ case MakeRoomView(kTSA16, kNorth):
+ if (GameState.getTSACommandCenterLocked())
+ return kCantOpenLocked;
+ break;
+ }
+
+ return Neighborhood::canOpenDoor(entry);
+}
+
+void FullTSA::bumpIntoWall() {
+ requestSpotSound(kTSABumpIntoWallIn, kTSABumpIntoWallOut, kFilterAllInput, 0);
+ Neighborhood::bumpIntoWall();
+}
+
+void FullTSA::downButton(const Input &input) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kTSA0B, kEast):
+ if (GameState.getTSA0BZoomedIn())
+ startExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kTSA0B, kNorth):
+ if (GameState.getTSA0BZoomedIn())
+ startExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kTSA0B, kWest):
+ if (GameState.getTSA0BZoomedIn() && GameState.getT0BMonitorMode() == kMonitorNeutral)
+ startExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ Neighborhood::downButton(input);
+ }
+}
+
+void FullTSA::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *spot) {
+ switch (spot->getObjectID()) {
+ case kTSA0BEastLeftRewindSpotID:
+ case kTSA0BEastLeftPlaySpotID:
+ if (_privateFlags.getFlag(kTSAPrivatePlayingRightComparisonFlag))
+ spot->setInactive();
+ else
+ Neighborhood::activateOneHotspot(entry, spot);
+ break;
+ case kTSA0BEastRightRewindSpotID:
+ case kTSA0BEastRightPlaySpotID:
+ if (_privateFlags.getFlag(kTSAPrivatePlayingLeftComparisonFlag))
+ spot->setInactive();
+ else
+ Neighborhood::activateOneHotspot(entry, spot);
+ break;
+ default:
+ Neighborhood::activateOneHotspot(entry, spot);
+ break;
+ }
+}
+
+void FullTSA::activateHotspots() {
+ Neighborhood::activateHotspots();
+
+ switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) {
+ case MakeRoomView(kTSA02, kNorth):
+ if (!GameState.getTSAFrontDoorUnlockedOutside())
+ _vm->getAllHotspots().activateOneHotspot(kTSA02DoorSpotID);
+ break;
+ case MakeRoomView(kTSA0B, kEast):
+ if (GameState.getTSA0BZoomedIn())
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerInstalledHistoricalLog:
+ case kTSABossSawHistoricalLog:
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ if (getCurrentActivation() != kActivateTSA0BComparisonVideo) {
+ _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareNoradSpotID);
+ _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareMarsSpotID);
+ _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareCaldoriaSpotID);
+ _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareWSCSpotID);
+ }
+ break;
+ }
+ break;
+ case MakeRoomView(kTSA0B, kNorth):
+ if (GameState.getTSA0BZoomedIn())
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToCommandCenterSpotID);
+ _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToReadyRoomSpotID);
+ _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToFrontDoorSpotID);
+ break;
+ }
+ break;
+ }
+}
+
+void FullTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+ switch (clickedSpot->getObjectID()) {
+ case kTSAGTOtherSpotID:
+ showExtraView(kTSAGTOtherChoice);
+ playSpotSoundSync(kTSANoOtherDestinationIn, kTSANoOtherDestinationOut);
+ showExtraView(kTSAGTCardSwipe);
+ break;
+ case kTSA02DoorSpotID:
+ GameState.setTSAFrontDoorUnlockedOutside(true);
+ Neighborhood::clickInHotspot(input, clickedSpot);
+ break;
+ case kTSA03EastJimenezSpotID:
+ startExtraLongSequence(kTSA03JimenezZoomIn, kTSA03JimenezZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA03WestCrenshawSpotID:
+ startExtraLongSequence(kTSA03CrenshawZoomIn, kTSA03CrenshawZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA04EastMatsumotoSpotID:
+ startExtraLongSequence(kTSA04MatsumotoZoomIn, kTSA04MatsumotoZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA04WestCastilleSpotID:
+ startExtraLongSequence(kTSA04CastilleZoomIn, kTSA04CastilleZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA05EastSinclairSpotID:
+ startExtraLongSequence(kTSA05SinclairZoomIn, kTSA05SinclairZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA05WestWhiteSpotID:
+ startExtraLongSequence(kTSA05WhiteZoomIn, kTSA05WhiteZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA0BEastCompareNoradSpotID:
+ initializeComparisonMonitor(kMonitorNoradComparison, kTSA0BNoradComparisonView);
+ break;
+ case kTSA0BEastCompareMarsSpotID:
+ initializeComparisonMonitor(kMonitorMarsComparison, kTSA0BMarsComparisonView);
+ break;
+ case kTSA0BEastCompareCaldoriaSpotID:
+ initializeComparisonMonitor(kMonitorCaldoriaComparison, kTSA0BCaldoriaComparisonView);
+ break;
+ case kTSA0BEastCompareWSCSpotID:
+ initializeComparisonMonitor(kMonitorWSCComparison, kTSA0BWSCComparisonView);
+ break;
+ case kTSA0BEastCloseVideoSpotID:
+ _navMovie.stop();
+ _sprite3.show();
+ _vm->delayShell(1, 2);
+ _sprite3.hide();
+ initializeComparisonMonitor(kMonitorNeutral, 0);
+ break;
+ case kTSA0BEastLeftPlaySpotID:
+ playLeftComparison();
+ break;
+ case kTSA0BEastRightPlaySpotID:
+ playRightComparison();
+ break;
+
+ // Command center
+ case kTSA0BWestTheorySpotID:
+ initializeTBPMonitor(kMonitorTheory, kTSA0BTBPTheoryHighlight);
+ break;
+ case kTSA0BWestBackgroundSpotID:
+ initializeTBPMonitor(kMonitorBackground, kTSA0BTBPBackgroundHighlight);
+ break;
+ case kTSA0BWestProcedureSpotID:
+ initializeTBPMonitor(kMonitorProcedure, kTSA0BTBPProcedureHighlight);
+ break;
+ case kTSA0BWestCloseVideoSpotID:
+ _navMovie.stop();
+ _sprite2.show();
+ _vm->delayShell(1, 2);
+ _sprite2.hide();
+ initializeTBPMonitor(kMonitorNeutral, 0);
+ break;
+ case kTSA0BWestPlayVideoSpotID:
+ playTBPMonitor();
+ break;
+ case kTSA0BEastLeftRewindSpotID:
+ case kTSA0BEastRightRewindSpotID:
+ case kTSA0BWestRewindVideoSpotID:
+ if ((GameState.getT0BMonitorMode() & kPlayingAnyMask) != 0) {
+ bool playing = _navMovie.isRunning();
+ if (playing)
+ _navMovie.stop();
+
+ if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID)
+ _sprite2.show();
+ else
+ _sprite1.show();
+
+ _vm->delayShell(1, 2);
+
+ if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID)
+ _sprite2.hide();
+ else
+ _sprite1.hide();
+
+ _navMovie.setTime(GameState.getT0BMonitorStart());
+
+ if (playing) {
+ _navMovie.start();
+ } else {
+ _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false);
+ _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false);
+ }
+ }
+ break;
+ case kTSA22EastMonitorSpotID:
+ requestExtraSequence(kTSA22RedEastZoomInSequence, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA23WestMonitorSpotID:
+ requestExtraSequence(kTSA23RedWestVaultZoomInSequence, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA0BNorthRobotsToCommandCenterSpotID:
+ _sprite1.setCurrentFrameIndex(kRedirectionCCDoorSprite);
+ _sprite1.show();
+ _vm->delayShell(1, 2);
+ _sprite1.hide();
+
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ // Nothing
+ break;
+ case kRobotsAtFrontDoor:
+ GameState.setTSAState(kRobotsAtCommandCenter);
+ _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite);
+ startExtraSequence(kTSA0BRobotsFromFrontDoorToCommandCenter, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kRobotsAtReadyRoom:
+ GameState.setTSAState(kRobotsAtCommandCenter);
+ _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite);
+ startExtraSequence(kTSA0BRobotsFromReadyRoomToCommandCenter, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ break;
+ case kTSA0BNorthRobotsToReadyRoomSpotID:
+ _sprite1.setCurrentFrameIndex(kRedirectionRRDoorSprite);
+ _sprite1.show();
+ _vm->delayShell(1, 2);
+ _sprite1.hide();
+
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ GameState.setTSAState(kRobotsAtReadyRoom);
+ _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite);
+ startExtraSequence(kTSA0BRobotsFromCommandCenterToReadyRoom, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kRobotsAtFrontDoor:
+ GameState.setTSAState(kRobotsAtReadyRoom);
+ _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite);
+ startExtraSequence(kTSA0BRobotsFromFrontDoorToReadyRoom, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kRobotsAtReadyRoom:
+ // Nothing
+ break;
+ }
+ break;
+ case kTSA0BNorthRobotsToFrontDoorSpotID:
+ _sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite);
+ _sprite1.show();
+ _vm->delayShell(1, 2);
+ _sprite1.hide();
+
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ GameState.setTSAState(kRobotsAtFrontDoor);
+ _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite);
+ startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kRobotsAtFrontDoor:
+ // Nothing
+ break;
+ case kRobotsAtReadyRoom:
+ GameState.setTSAState(kRobotsAtFrontDoor);
+ _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite);
+ startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ break;
+
+ // Pegasus
+ case kTSA37NorthJumpToPrehistoricSpotID:
+ startExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA37NorthExitSpotID:
+ _sprite2.setCurrentFrameIndex(1);
+ _vm->delayShell(1, 2);
+ releaseSprites();
+ moveForward();
+ break;
+ case kTSA37NorthJumpMenuSpotID:
+ _sprite2.setCurrentFrameIndex(1);
+ _vm->delayShell(1, 2);
+ releaseSprites();
+ break;
+ case kTSA37NorthJumpToNoradSpotID:
+ GameState.setTSAState(kPlayerOnWayToNorad);
+ requestExtraSequence(kTSA37JumpToNorad, 0, kFilterNoInput);
+
+ if (!GameState.getBeenToNorad()) {
+ requestExtraSequence(kTSA37NoradToAI7, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37PegasusAI7, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37AI7ToNorad, 0, kFilterNoInput);
+ GameState.setBeenToNorad(true);
+ }
+
+ requestExtraSequence(kTSA37NoradToDepart, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA37NorthJumpToMarsSpotID:
+ GameState.setTSAState(kPlayerOnWayToMars);
+ requestExtraSequence(kTSA37JumpToMars, 0, kFilterNoInput);
+
+ if (!GameState.getBeenToMars()) {
+ requestExtraSequence(kTSA37MarsToAI6, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37PegasusAI6, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37AI6ToMars, 0, kFilterNoInput);
+ GameState.setBeenToMars(true);
+ }
+
+ requestExtraSequence(kTSA37MarsToDepart, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA37NorthJumpToWSCSpotID:
+ GameState.setTSAState(kPlayerOnWayToWSC);
+ requestExtraSequence(kTSA37JumpToWSC, 0, kFilterNoInput);
+
+ if (!GameState.getBeenToWSC()) {
+ requestExtraSequence(kTSA37WSCToAI5, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37PegasusAI5, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37AI5ToWSC, 0, kFilterNoInput);
+ GameState.setBeenToWSC(true);
+ }
+
+ requestExtraSequence(kTSA37WSCToDepart, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ Neighborhood::clickInHotspot(input, clickedSpot);
+ break;
+ }
+}
+
+void FullTSA::showMainJumpMenu() {
+ ExtraID jumpMenuView = kTSA37JumpMenu000;
+
+ if (GameState.getNoradFinished())
+ jumpMenuView += 4;
+ if (GameState.getMarsFinished())
+ jumpMenuView += 2;
+ if (GameState.getWSCFinished())
+ jumpMenuView += 1;
+
+ showExtraView(jumpMenuView);
+ setCurrentActivation(kActivationMainJumpMenu);
+}
+
+void FullTSA::playTBPMonitor() {
+ InputDevice.waitInput(kFilterAllButtons);
+
+ if ((GameState.getT0BMonitorMode() & kPlayingTBPMask) == 0) {
+ ExtraID extra;
+
+ switch (GameState.getT0BMonitorMode() & kRawModeMask) {
+ case kMonitorTheory:
+ GameState.setTSASeenTheory(true);
+ extra = kTSA0BTBPTheory;
+ GameState.setScoringSawTheory(true);
+ break;
+ case kMonitorBackground:
+ GameState.setTSASeenBackground(true);
+ extra = kTSA0BTBPBackground;
+ GameState.setScoringSawBackground(true);
+ break;
+ case kMonitorProcedure:
+ GameState.setTSASeenProcedure(true);
+ extra = kTSA0BTBPProcedure;
+ GameState.setScoringSawProcedure(true);
+ break;
+ default:
+ error("Invalid monitor mode");
+ }
+
+ GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingTBPMask);
+
+ ExtraTable::Entry entry;
+ getExtraEntry(extra, entry);
+ _lastExtra = extra;
+
+ GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5);
+ startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, kExtraCompletedFlag, false, kFilterAllInput);
+ } else if (_navMovie.isRunning()) {
+ _navMovie.stop();
+ } else {
+ _navMovie.start();
+ }
+}
+
+void FullTSA::initializeTBPMonitor(const int newMode, const ExtraID highlightExtra) {
+ GameState.setT0BMonitorMode(newMode);
+
+ if (newMode != kMonitorNeutral) {
+ showExtraView(highlightExtra);
+ _vm->delayShell(1, 2);
+ setCurrentActivation(kActivateTSA0BTBPVideo);
+ _sprite1.addPICTResourceFrame(kTBPRewindPICTID, false, 0, 0);
+ _sprite1.moveElementTo(kTBPRewindLeft, kTBPRewindTop);
+ _sprite1.setCurrentFrameIndex(0);
+ _sprite2.addPICTResourceFrame(kTBPCloseBoxPICTID, false, 0, 0);
+ _sprite2.moveElementTo(kTBPCloseLeft, kTBPCloseTop);
+ _sprite2.setCurrentFrameIndex(0);
+ playTBPMonitor();
+ } else {
+ if (GameState.getTSAState() == kTSAPlayerForcedReview && GameState.getTSASeenTheory() &&
+ GameState.getTSASeenBackground() && GameState.getTSASeenProcedure()) {
+ setOffRipAlarm();
+ } else {
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+ updateViewFrame();
+ }
+
+ releaseSprites();
+ }
+
+ _interruptionFilter = kFilterAllInput;
+}
+
+void FullTSA::startUpComparisonMonitor() {
+ releaseSprites();
+
+ _sprite1.addPICTResourceFrame(kComparisonHiliteNoradPICTID, false,
+ kComparisonHiliteNoradLeft - kComparisonHiliteSpriteLeft,
+ kComparisonHiliteNoradTop - kComparisonHiliteSpriteTop);
+ _sprite1.addPICTResourceFrame(kComparisonHiliteMarsPICTID, false,
+ kComparisonHiliteMarsLeft - kComparisonHiliteSpriteLeft,
+ kComparisonHiliteMarsTop - kComparisonHiliteSpriteTop);
+ _sprite1.addPICTResourceFrame(kComparisonHiliteCaldoriaPICTID, false,
+ kComparisonHiliteCaldoriaLeft - kComparisonHiliteSpriteLeft,
+ kComparisonHiliteCaldoriaTop - kComparisonHiliteSpriteTop);
+ _sprite1.addPICTResourceFrame(kComparisonHiliteWSCPICTID, false,
+ kComparisonHiliteWSCLeft - kComparisonHiliteSpriteLeft,
+ kComparisonHiliteWSCTop - kComparisonHiliteSpriteTop);
+
+ _sprite1.setCurrentFrameIndex(0);
+ _sprite1.moveElementTo(kComparisonHiliteSpriteLeft, kComparisonHiliteSpriteTop);
+
+ _sprite2.addPICTResourceFrame(kComparisonChancesNoradPICTID, false,
+ kComparisonChancesNoradLeft - kComparisonChancesSpriteLeft,
+ kComparisonChancesNoradTop - kComparisonChancesSpriteTop);
+ _sprite2.addPICTResourceFrame(kComparisonChancesMarsPICTID, false,
+ kComparisonChancesMarsLeft - kComparisonChancesSpriteLeft,
+ kComparisonChancesMarsTop - kComparisonChancesSpriteTop);
+ _sprite2.addPICTResourceFrame(kComparisonChancesCaldoriaPICTID, false,
+ kComparisonChancesCaldoriaLeft - kComparisonChancesSpriteLeft,
+ kComparisonChancesCaldoriaTop - kComparisonChancesSpriteTop);
+ _sprite2.addPICTResourceFrame(kComparisonChancesWSCPICTID, false,
+ kComparisonChancesWSCLeft - kComparisonChancesSpriteLeft,
+ kComparisonChancesWSCTop - kComparisonChancesSpriteTop);
+
+ _sprite2.setCurrentFrameIndex(0);
+ _sprite2.moveElementTo(kComparisonChancesSpriteLeft, kComparisonChancesSpriteTop);
+ updateViewFrame();
+}
+
+void FullTSA::shutDownComparisonMonitor() {
+ releaseSprites();
+}
+
+void FullTSA::initializeComparisonMonitor(const int newMode, const ExtraID comparisonView) {
+ GameState.setT0BMonitorMode(newMode);
+ _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false);
+ _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false);
+
+ if (newMode != kMonitorNeutral) {
+ shutDownComparisonMonitor();
+ setCurrentActivation(kActivateTSA0BComparisonVideo);
+ _sprite1.addPICTResourceFrame(kComparisonLeftRewindPICTID, false, 0, 0);
+ _sprite1.moveElementTo(kComparisonLeftRewindLeft, kComparisonLeftRewindTop);
+ _sprite1.setCurrentFrameIndex(0);
+ _sprite2.addPICTResourceFrame(kComparisonRightRewindPICTID, false, 0, 0);
+ _sprite2.moveElementTo(kComparisonRightRewindLeft, kComparisonRightRewindTop);
+ _sprite2.setCurrentFrameIndex(0);
+ _sprite3.addPICTResourceFrame(kComparisonCloseBoxPICTID, false, 0, 0);
+ _sprite3.moveElementTo(kComparisonCloseLeft, kComparisonCloseTop);
+ _sprite3.setCurrentFrameIndex(0);
+ showExtraView(comparisonView);
+ } else {
+ if (GameState.getTSAState() == kTSAPlayerInstalledHistoricalLog &&
+ GameState.getTSASeenNoradNormal() &&
+ GameState.getTSASeenNoradAltered() &&
+ GameState.getTSASeenMarsNormal() &&
+ GameState.getTSASeenMarsAltered() &&
+ GameState.getTSASeenCaldoriaNormal() &&
+ GameState.getTSASeenCaldoriaAltered() &&
+ GameState.getTSASeenWSCNormal() &&
+ GameState.getTSASeenWSCAltered()) {
+ GameState.setTSAState(kTSABossSawHistoricalLog);
+ requestExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kTSA0BEastTurnLeft, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+ releaseSprites();
+ startUpComparisonMonitor();
+ }
+ }
+
+ _interruptionFilter = kFilterAllInput;
+}
+
+void FullTSA::playLeftComparison() {
+ InputDevice.waitInput(kFilterAllButtons);
+
+ if ((GameState.getT0BMonitorMode() & kPlayingLeftComparisonMask) == 0) {
+ ExtraID extra;
+
+ switch (GameState.getT0BMonitorMode() & kRawModeMask) {
+ case kMonitorNoradComparison:
+ GameState.setTSASeenNoradAltered(true);
+ extra = kTSA0BNoradAltered;
+ GameState.setScoringSawNoradAltered(true);
+ break;
+ case kMonitorMarsComparison:
+ GameState.setTSASeenMarsAltered(true);
+ extra = kTSA0BMarsAltered;
+ GameState.setScoringSawMarsAltered(true);
+ break;
+ case kMonitorCaldoriaComparison:
+ GameState.setTSASeenCaldoriaAltered(true);
+ extra = kTSA0BCaldoriaAltered;
+ GameState.setScoringSawCaldoriaAltered(true);
+ break;
+ case kMonitorWSCComparison:
+ GameState.setTSASeenWSCAltered(true);
+ extra = kTSA0BWSCAltered;
+ GameState.setScoringSawWSCAltered(true);
+ break;
+ default:
+ error("Invalid monitor mode");
+ }
+
+ GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingLeftComparisonMask);
+
+ ExtraTable::Entry entry;
+ getExtraEntry(extra, entry);
+ _lastExtra = extra;
+
+ // skip first five frames of movie
+ // (this is a dissolve that doesn't belong...)
+ GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5);
+ _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag);
+
+ // Allow clicking...
+ startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd,
+ kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter());
+ } else if (_navMovie.isRunning()) {
+ _navMovie.stop();
+ } else {
+ _navMovie.start();
+ }
+}
+
+void FullTSA::playRightComparison() {
+ InputDevice.waitInput(kFilterAllButtons);
+
+ if ((GameState.getT0BMonitorMode() & kPlayingRightComparisonMask) == 0) {
+ ExtraID extra;
+
+ switch (GameState.getT0BMonitorMode() & kRawModeMask) {
+ case kMonitorNoradComparison:
+ GameState.setTSASeenNoradNormal(true);
+ extra = kTSA0BNoradUnaltered;
+ GameState.setScoringSawNoradNormal(true);
+ break;
+ case kMonitorMarsComparison:
+ GameState.setTSASeenMarsNormal(true);
+ extra = kTSA0BMarsUnaltered;
+ GameState.setScoringSawMarsNormal(true);
+ break;
+ case kMonitorCaldoriaComparison:
+ GameState.setTSASeenCaldoriaNormal(true);
+ extra = kTSA0BCaldoriaUnaltered;
+ GameState.setScoringSawCaldoriaNormal(true);
+ break;
+ case kMonitorWSCComparison:
+ GameState.setTSASeenWSCNormal(true);
+ extra = kTSA0BWSCUnaltered;
+ GameState.setScoringSawWSCNormal(true);
+ break;
+ default:
+ error("Invalid monitor mode");
+ }
+
+ GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingRightComparisonMask);
+
+ ExtraTable::Entry entry;
+ getExtraEntry(extra, entry);
+ _lastExtra = extra;
+
+ // skip first five frames of movie
+ // (this is a dissolve that doesn't belong...)
+ GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5);
+ _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag);
+
+ // Allow clicking...
+ startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd,
+ kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter());
+ } else if (_navMovie.isRunning()) {
+ _navMovie.stop();
+ } else {
+ _navMovie.start();
+ }
+}
+
+// When this function is called, the player is zoomed up on the center monitor, and the
+// TSA state is kTSABossSawHistoricalLog.
+void FullTSA::startRobotGame() {
+ requestExtraSequence(kTSA0BNorthCantChangeHistory, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BAIInterruption, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BShowGuardRobots, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BRobotsToCommandCenter, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void FullTSA::startUpRobotMonitor() {
+ releaseSprites();
+
+ _sprite1.addPICTResourceFrame(kRedirectionCCRolloverPICTID, true,
+ kRedirectionCCRolloverLeft - kRedirectionSprite1Left,
+ kRedirectionCCRolloverTop - kRedirectionSprite1Top);
+ _sprite1.addPICTResourceFrame(kRedirectionRRRolloverPICTID, true,
+ kRedirectionRRRolloverLeft - kRedirectionSprite1Left,
+ kRedirectionRRRolloverTop - kRedirectionSprite1Top);
+ _sprite1.addPICTResourceFrame(kRedirectionFDRolloverPICTID, false,
+ kRedirectionFDRolloverLeft - kRedirectionSprite1Left,
+ kRedirectionFDRolloverTop - kRedirectionSprite1Top);
+ _sprite1.addPICTResourceFrame(kRedirectionCCDoorPICTID, true,
+ kRedirectionCCDoorLeft - kRedirectionSprite1Left,
+ kRedirectionCCDoorTop - kRedirectionSprite1Top);
+ _sprite1.addPICTResourceFrame(kRedirectionRRDoorPICTID, true,
+ kRedirectionRRDoorLeft - kRedirectionSprite1Left,
+ kRedirectionRRDoorTop - kRedirectionSprite1Top);
+ _sprite1.addPICTResourceFrame(kRedirectionFDDoorPICTID, false,
+ kRedirectionFDDoorLeft - kRedirectionSprite1Left,
+ kRedirectionFDDoorTop - kRedirectionSprite1Top);
+ _sprite1.addPICTResourceFrame(kRedirectionClosePICTID, false,
+ kRedirectionCloseLeft - kRedirectionSprite1Left,
+ kRedirectionCloseTop - kRedirectionSprite1Top);
+ _sprite1.moveElementTo(kRedirectionSprite1Left, kRedirectionSprite1Top);
+
+ _sprite2.addPICTResourceFrame(kRedirectionSecuredPICTID, false,
+ kRedirectionSecuredLeft - kRedirectionSprite2Left,
+ kRedirectionSecuredTop - kRedirectionSprite2Top);
+ _sprite2.addPICTResourceFrame(kRedirectionNewTargetPICTID, false,
+ kRedirectionNewTargetLeft - kRedirectionSprite2Left,
+ kRedirectionNewTargetTop - kRedirectionSprite2Top);
+ _sprite2.moveElementTo(kRedirectionSprite2Left, kRedirectionSprite2Top);
+
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ showExtraView(kTSA0BNorthRobotsAtCCView);
+ break;
+ case kRobotsAtFrontDoor:
+ showExtraView(kTSA0BNorthRobotsAtFDView);
+ break;
+ case kRobotsAtReadyRoom:
+ showExtraView(kTSA0BNorthRobotsAtRRView);
+ break;
+ }
+}
+
+void FullTSA::shutDownRobotMonitor() {
+ releaseSprites();
+}
+
+// Assume this is called only when zoomed in at T0B west
+void FullTSA::setOffRipAlarm() {
+ GameState.setTSAState(kTSAPlayerDetectedRip);
+ _ripTimer.initImage();
+ _ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop);
+ _ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale);
+ _ripTimer.start();
+ loadAmbientLoops();
+ startExtraSequenceSync(kTSA0BRipAlarmScreen, kFilterNoInput);
+ _vm->delayShell(2, 1); // Two seconds..
+ requestExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kTSA0BWestTurnRight, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kTSA0BNorthFinallyHappened, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void FullTSA::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kTSA04, kNorth):
+ case MakeRoomView(kTSA14, kEast):
+ case MakeRoomView(kTSA15, kWest):
+ case MakeRoomView(kTSA16, kNorth):
+ case MakeRoomView(kTSA16, kSouth):
+ case MakeRoomView(kTSA21Cyan, kSouth):
+ case MakeRoomView(kTSA21Red, kSouth):
+ case MakeRoomView(kTSA26, kNorth):
+ makeContinuePoint();
+ break;
+ }
+}
+
+void FullTSA::arriveAt(const RoomID room, const DirectionConstant direction) {
+ checkRobotLocations(room, direction);
+ Neighborhood::arriveAt(room, direction);
+
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kTSADeathRoom, kNorth):
+ case MakeRoomView(kTSADeathRoom, kSouth):
+ case MakeRoomView(kTSADeathRoom, kEast):
+ case MakeRoomView(kTSADeathRoom, kWest):
+ die(kDeathShotByTSARobots);
+ break;
+ case MakeRoomView(kTSA00, kNorth):
+ if (GameState.getLastNeighborhood() != kFullTSAID) {
+ makeContinuePoint();
+ openDoor();
+ } else {
+ setCurrentActivation(kActivateTSAReadyForCard);
+ loopExtraSequence(kTSATransporterArrowLoop, 0);
+ }
+ break;
+ case MakeRoomView(kTSA03, kNorth):
+ case MakeRoomView(kTSA05, kNorth):
+ case MakeRoomView(kTSA0A, kNorth):
+ case MakeRoomView(kTSA06, kNorth):
+ case MakeRoomView(kTSA07, kNorth):
+ if (_utilityFuse.isFuseLit())
+ _utilityFuse.stopFuse();
+ GameState.setScoringEnterTSA(true);
+ break;
+ case MakeRoomView(kTSA04, kNorth):
+ if (_utilityFuse.isFuseLit())
+ _utilityFuse.stopFuse();
+ if (!GameState.getTSASeenRobotGreeting())
+ startExtraSequence(kTSA04NorthRobotGreeting, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kTSA03, kSouth):
+ GameState.setTSAFrontDoorUnlockedInside(GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished());
+ break;
+ case MakeRoomView(kTSA0A, kEast):
+ case MakeRoomView(kTSA0A, kWest):
+ if (GameState.getTSAState() == kTSAPlayerNotArrived)
+ setCurrentActivation(kActivateTSARobotsAwake);
+ break;
+ case MakeRoomView(kTSA0B, kNorth):
+ if (GameState.getTSA0BZoomedIn()) {
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerNeedsHistoricalLog:
+ _ripTimer.show();
+ break;
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ startUpRobotMonitor();
+ break;
+ }
+ } else {
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerNotArrived:
+ requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kTSA0BNorthYoureBusted, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kTSA0BNorthTurnLeft, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BWestZoomIn, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSAPlayerGotHistoricalLog:
+ startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ }
+ break;
+ case MakeRoomView(kTSA0B, kSouth):
+ GameState.setTSA0BZoomedIn(false);
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+ break;
+ case MakeRoomView(kTSA0B, kWest):
+ if (GameState.getTSA0BZoomedIn()) {
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+ initializeTBPMonitor(kMonitorNeutral, 0);
+ } else {
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+ }
+ break;
+ case MakeRoomView(kTSA0B, kEast):
+ if (GameState.getTSA0BZoomedIn()) {
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerInstalledHistoricalLog:
+ case kTSABossSawHistoricalLog:
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ initializeComparisonMonitor(kMonitorNeutral, 0);
+ break;
+ }
+ } else {
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+ }
+ break;
+ case MakeRoomView(kTSA21Red, kSouth):
+ if (GameState.getTSAState() == kRobotsAtFrontDoor)
+ GameState.setScoringWentToReadyRoom2(true);
+ break;
+ case MakeRoomView(kTSA22Red, kEast):
+ if (!_vm->playerHasItemID(kJourneymanKey))
+ setCurrentActivation(kActivationDoesntHaveKey);
+ break;
+ case MakeRoomView(kTSA23Red, kWest):
+ if (!_vm->playerHasItemID(kPegasusBiochip))
+ setCurrentActivation(kActivationDoesntHaveChips);
+ break;
+ case MakeRoomView(kTSA25Red, kNorth):
+ arriveAtTSA25Red();
+ break;
+ case MakeRoomView(kTSA34, kSouth):
+ if (GameState.getLastRoom() == kTSA37)
+ closeDoorOffScreen(kTSA37, kNorth);
+ break;
+ case MakeRoomView(kTSA37, kNorth):
+ arriveAtTSA37();
+ break;
+ }
+}
+
+void FullTSA::checkRobotLocations(const RoomID room, const DirectionConstant dir) {
+ switch (room) {
+ case kTSA03:
+ case kTSA04:
+ case kTSA05:
+ case kTSA06:
+ case kTSA0A:
+ case kTSA07:
+ case kTSA08:
+ case kTSA09:
+ case kTSA10:
+ case kTSA11:
+ case kTSA12:
+ case kTSA13:
+ case kTSA14:
+ case kTSA15:
+ switch (GameState.getTSAState()) {
+ case kRobotsAtFrontDoor:
+ setCurrentAlternate(kAltTSARobotsAtFrontDoor);
+ break;
+ case kRobotsAtReadyRoom:
+ setCurrentAlternate(kAltTSARobotsAtReadyRoom);
+ break;
+ }
+ break;
+ case kTSA16:
+ if (dir == kNorth) {
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) {
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption);
+ _privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true);
+ }
+ break;
+ case kRobotsAtFrontDoor:
+ setCurrentAlternate(kAltTSARobotsAtFrontDoor);
+ break;
+ case kRobotsAtReadyRoom:
+ setCurrentAlternate(kAltTSARobotsAtReadyRoom);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+void FullTSA::arriveAtTSA25Red() {
+ if (!_vm->playerHasItemID(kJourneymanKey))
+ startExtraSequence(kTSA25NorthDeniedNoKey, kExtraCompletedFlag, kFilterNoInput);
+ else if (!_vm->playerHasItemID(kPegasusBiochip))
+ startExtraSequence(kTSA25NorthDeniedNoChip, kExtraCompletedFlag, kFilterNoInput);
+ else if (GameState.getTSABiosuitOn())
+ startExtraSequence(kTSA25NorthAlreadyHaveSuit, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kTSA25NorthPutOnSuit, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void FullTSA::arriveAtTSA37() {
+ _ripTimer.stop();
+ _ripTimer.releaseImage();
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerNeedsHistoricalLog:
+ startExtraLongSequence(kTSA37HorseToAI1, kTSA37AI2ToPrehistoric, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kPlayerOnWayToPrehistoric:
+ setCurrentActivation(kActivationJumpToPrehistoric);
+ showExtraView(kTSA37AI2ToPrehistoric);
+ break;
+ case kTSAPlayerGotHistoricalLog:
+ initializePegasusButtons(false);
+ break;
+ case kPlayerWentToPrehistoric:
+ case kPlayerOnWayToNorad:
+ case kPlayerOnWayToMars:
+ case kPlayerOnWayToWSC:
+ startExtraSequence(kTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kRobotsAtFrontDoor:
+ startExtraLongSequence(kTSA37HorseToColonel2, kTSA37AI4ToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kPlayerLockedInPegasus:
+ showMainJumpMenu();
+ break;
+ case kPlayerFinishedWithTSA:
+ initializePegasusButtons(true);
+ break;
+ }
+}
+
+void FullTSA::turnTo(const DirectionConstant newDirection) {
+ Neighborhood::turnTo(newDirection);
+
+ switch (MakeRoomView(GameState.getCurrentRoom(), newDirection)) {
+ case MakeRoomView(kTSA03, kSouth):
+ if (GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished())
+ GameState.setTSAFrontDoorUnlockedInside(true);
+ else
+ GameState.setTSAFrontDoorUnlockedInside(false);
+ break;
+ case MakeRoomView(kTSA0A, kEast):
+ case MakeRoomView(kTSA0A, kWest):
+ setCurrentActivation(kActivateTSARobotsAwake);
+ break;
+ case MakeRoomView(kTSA0B, kEast):
+ if (GameState.getTSA0BZoomedIn())
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+ else
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+
+ GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask);
+
+ if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag))
+ _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false);
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerInstalledHistoricalLog:
+ case kTSABossSawHistoricalLog:
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ if (GameState.getTSA0BZoomedIn())
+ startUpComparisonMonitor();
+ break;
+ }
+ break;
+ case MakeRoomView(kTSA0B, kNorth):
+ if (GameState.getTSA0BZoomedIn())
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+ else
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+
+ GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask);
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerNeedsHistoricalLog:
+ if (GameState.getTSA0BZoomedIn())
+ _ripTimer.show();
+ break;
+ case kTSAPlayerGotHistoricalLog:
+ if (!GameState.getTSA0BZoomedIn())
+ startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSAPlayerInstalledHistoricalLog:
+ if (GameState.getTSA0BZoomedIn()) {
+ if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) &&
+ (GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) &&
+ (GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) &&
+ (GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) {
+ GameState.setTSAState(kTSABossSawHistoricalLog);
+ startRobotGame();
+ }
+ }
+ break;
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ if (GameState.getTSA0BZoomedIn())
+ startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ break;
+ case MakeRoomView(kTSA0B, kWest):
+ if (GameState.getTSA0BZoomedIn())
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+ else
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+
+ GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask);
+
+ if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag))
+ _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false);
+
+ if (GameState.getTSA0BZoomedIn())
+ initializeTBPMonitor(kMonitorNeutral, 0);
+ break;
+ case MakeRoomView(kTSA0B, kSouth):
+ GameState.setTSA0BZoomedIn(false);
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+ break;
+ case MakeRoomView(kTSA16, kNorth):
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) {
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption);
+ _privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true);
+ }
+ break;
+ case kRobotsAtFrontDoor:
+ setCurrentAlternate(kAltTSARobotsAtFrontDoor);
+ break;
+ case kRobotsAtReadyRoom:
+ setCurrentAlternate(kAltTSARobotsAtReadyRoom);
+ break;
+ }
+ break;
+ case MakeRoomView(kTSA22Red, kEast):
+ if (!_vm->playerHasItemID(kJourneymanKey))
+ setCurrentActivation(kActivationDoesntHaveKey);
+ break;
+ case MakeRoomView(kTSA22Red, kNorth):
+ case MakeRoomView(kTSA22Red, kSouth):
+ if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) {
+ playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut);
+ _privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, false);
+ }
+
+ setCurrentActivation(kActivateHotSpotAlways);
+ break;
+ case MakeRoomView(kTSA23Red, kWest):
+ if (!_vm->playerHasItemID(kPegasusBiochip))
+ setCurrentActivation(kActivationDoesntHaveChips);
+ break;
+ case MakeRoomView(kTSA23Red, kNorth):
+ case MakeRoomView(kTSA23Red, kSouth):
+ if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) {
+ playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut);
+ _privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, false);
+ }
+
+ setCurrentActivation(kActivateHotSpotAlways);
+ break;
+ }
+
+ // Make sure the TBP monitor is forced neutral.
+ GameState.setT0BMonitorMode(kMonitorNeutral);
+}
+
+void FullTSA::closeDoorOffScreen(const RoomID room, const DirectionConstant) {
+ switch (room) {
+ case kTSA00:
+ case kTSA01:
+ if (GameState.getCurrentRoom() == kTSA01 || GameState.getCurrentRoom() == kTSA02)
+ playSpotSoundSync(kTSAGTDoorCloseIn, kTSAGTDoorCloseOut);
+ break;
+ case kTSA02:
+ case kTSA03:
+ playSpotSoundSync(kTSAEntryDoorCloseIn, kTSAEntryDoorCloseOut);
+ break;
+ case kTSA14:
+ case kTSA15:
+ case kTSA16:
+ case kTSA21Cyan:
+ case kTSA21Red:
+ playSpotSoundSync(kTSAInsideDoorCloseIn, kTSAInsideDoorCloseOut);
+ break;
+ case kTSA34:
+ case kTSA37:
+ playSpotSoundSync(kTSAPegasusDoorCloseIn, kTSAPegasusDoorCloseOut);
+ break;
+ }
+}
+
+void FullTSA::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ ExtraID lastExtra = _lastExtra;
+
+ if ((flags & kExtraCompletedFlag) != 0) {
+ switch (lastExtra) {
+ case kTSA0BEastTurnLeft:
+ // Need to check this here because turnTo will call _navMovie.stop,
+ // so it has to happen before Neighborhood::receiveNotification,
+ // which may end up starting another sequence...
+ turnTo(kNorth);
+ break;
+ }
+ }
+
+ Neighborhood::receiveNotification(notification, flags);
+
+ InventoryItem *item;
+
+ if ((flags & kExtraCompletedFlag) != 0) {
+ // Only allow input if we're not in the middle of series of queue requests.
+ if (actionQueueEmpty())
+ _interruptionFilter = kFilterAllInput;
+
+ switch (lastExtra) {
+ case kTSAGTCardSwipe:
+ item = (InventoryItem *)_vm->getAllItems().findItemByID(kKeyCard);
+ _vm->addItemToInventory(item);
+ setCurrentActivation(kActivateTSAReadyToTransport);
+ break;
+ case kTSAGTGoToCaldoria:
+ _vm->jumpToNewEnvironment(kCaldoriaID, kCaldoria44, kEast);
+
+ if (GameState.allTimeZonesFinished())
+ GameState.setScoringWentAfterSinclair(true);
+ break;
+ case kTSAGTGoToTokyo:
+ case kTSAGTGoToBeach:
+ if (GameState.allTimeZonesFinished())
+ die(kDeathSinclairShotDelegate);
+ else
+ die(kDeathUncreatedInTSA);
+ break;
+ case kTSA02NorthZoomOut:
+ openDoor();
+ break;
+
+ // Hall of suspects.
+ case kTSA04NorthRobotGreeting:
+ GameState.setTSASeenRobotGreeting(true);
+ restoreStriding(kTSA03, kNorth, kNoAlternateID);
+ break;
+ case kTSA03JimenezZoomIn:
+ GameState.setScoringSawBust1(true);
+ break;
+ case kTSA03CrenshawZoomIn:
+ GameState.setScoringSawBust2(true);
+ break;
+ case kTSA04MatsumotoZoomIn:
+ GameState.setScoringSawBust3(true);
+ break;
+ case kTSA04CastilleZoomIn:
+ GameState.setScoringSawBust4(true);
+ break;
+ case kTSA05SinclairZoomIn:
+ GameState.setScoringSawBust5(true);
+ break;
+ case kTSA05WhiteZoomIn:
+ GameState.setScoringSawBust6(true);
+ break;
+
+ // Command center
+ // Historical comparison...
+ case kTSA0BEastZoomIn:
+ GameState.setTSA0BZoomedIn(true);
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+ GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask);
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerInstalledHistoricalLog:
+ case kTSABossSawHistoricalLog:
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ startUpComparisonMonitor();
+ break;
+ }
+ break;
+ case kTSA0BEastZoomOut:
+ GameState.setTSA0BZoomedIn(false);
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+
+ switch (GameState.getTSAState()) {
+ case kTSABossSawHistoricalLog:
+ // Prevent current view from activating.
+ break;
+ default:
+ activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(),
+ kSpotOnTurnMask);
+ break;
+ }
+ break;
+ case kTSA0BComparisonStartup:
+ if ((flags & kActionRequestCompletedFlag) != 0) {
+ _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false);
+ GameState.setTSAState(kTSAPlayerInstalledHistoricalLog);
+ turnTo(kEast);
+ }
+
+ startUpComparisonMonitor();
+ break;
+ case kTSA0BNoradAltered:
+ case kTSA0BMarsAltered:
+ case kTSA0BCaldoriaAltered:
+ case kTSA0BWSCAltered:
+ case kTSA0BNoradUnaltered:
+ case kTSA0BMarsUnaltered:
+ case kTSA0BCaldoriaUnaltered:
+ case kTSA0BWSCUnaltered:
+ initializeComparisonMonitor(kMonitorNeutral, 0);
+ break;
+
+ // Center monitor.
+ case kTSA0BNorthZoomIn:
+ GameState.setTSA0BZoomedIn(true);
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+ GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask);
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerNeedsHistoricalLog:
+ startExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSABossSawHistoricalLog:
+ case kTSAPlayerInstalledHistoricalLog:
+ if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) &&
+ (GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) &&
+ (GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) &&
+ (GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) {
+ GameState.setTSAState(kTSABossSawHistoricalLog);
+ startRobotGame();
+ }
+ break;
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ break;
+ case kTSA0BNorthZoomOut:
+ GameState.setTSA0BZoomedIn(false);
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+ break;
+ case kTSA0BShowRip1:
+ GameState.setTSAState(kTSAPlayerNeedsHistoricalLog);
+ GameState.setTSACommandCenterLocked(false);
+
+ if ((flags & kActionRequestCompletedFlag) != 0)
+ turnTo(kNorth);
+
+ _ripTimer.show();
+ break;
+ case kTSA0BNorthHistLogOpen:
+ setCurrentActivation(kActivationLogReaderOpen);
+ _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, true);
+ break;
+ case kTSA0BRobotsToCommandCenter:
+ GameState.setTSAState(kRobotsAtCommandCenter);
+ // Fall through
+ case kTSA0BShowGuardRobots:
+ startUpRobotMonitor();
+ // Fall through
+ case kTSA0BRobotsFromCommandCenterToReadyRoom:
+ case kTSA0BRobotsFromReadyRoomToCommandCenter:
+ case kTSA0BRobotsFromCommandCenterToFrontDoor:
+ case kTSA0BRobotsFromFrontDoorToCommandCenter:
+ case kTSA0BRobotsFromFrontDoorToReadyRoom:
+ case kTSA0BRobotsFromReadyRoomToFrontDoor:
+ _sprite2.setCurrentFrameIndex(kRedirectionSecuredSprite);
+ _sprite2.show();
+ break;
+
+ // TBP monitor.
+ case kTSA0BWestZoomIn:
+ GameState.setTSA0BZoomedIn(true);
+ setCurrentActivation(kActivateTSA0BZoomedIn);
+
+ if (GameState.getTSAState() == kTSAPlayerNotArrived) {
+ turnTo(kWest);
+ GameState.setTSACommandCenterLocked(true);
+ GameState.setTSAState(kTSAPlayerForcedReview);
+ }
+
+ initializeTBPMonitor(kMonitorNeutral, 0);
+ break;
+ case kTSA0BWestZoomOut:
+ GameState.setTSA0BZoomedIn(false);
+ setCurrentActivation(kActivateTSA0BZoomedOut);
+ GameState.setT0BMonitorMode(kMonitorNeutral);
+
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerDetectedRip:
+ // Keep the current view from activating.
+ break;
+ default:
+ activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(),
+ kSpotOnTurnMask);
+ break;
+ }
+ break;
+ case kTSA0BTBPTheory:
+ case kTSA0BTBPBackground:
+ case kTSA0BTBPProcedure:
+ initializeTBPMonitor(kMonitorNeutral, 0);
+ break;
+
+ // Ready room
+ case kTSA22RedEastZoomInSequence:
+ _privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, true);
+ setCurrentActivation(kActivationKeyVaultOpen);
+ break;
+ case kTSA23RedWestVaultZoomInSequence:
+ _privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, true);
+ setCurrentActivation(kActivationChipVaultOpen);
+ break;
+ case kTSA25NorthPutOnSuit:
+ GameState.setTSABiosuitOn(true);
+ GameState.setScoringGotBiosuit(true);
+ // Fall through...
+ case kTSA25NorthAlreadyHaveSuit:
+ requestExtraSequence(kTSA25NorthDescending1, 0, kFilterNoInput);
+ requestExtraSequence(kTSA25NorthDescending2, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTSA25NorthDescending2:
+ arriveAt(kTSA26, kNorth);
+ break;
+
+ // Pegasus.
+ case kTSA37HorseToAI1:
+ case kTSA37AI2ToPrehistoric:
+ setCurrentActivation(kActivationJumpToPrehistoric);
+ GameState.setTSAState(kPlayerOnWayToPrehistoric);
+ break;
+ case kTSA37PegasusDepart:
+ _vm->setLastEnergyValue(kFullEnergy);
+
+ switch (GameState.getTSAState()) {
+ case kPlayerOnWayToPrehistoric:
+ _vm->jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth);
+ GameState.setPrehistoricSeenTimeStream(false);
+ GameState.setPrehistoricSeenFlyer1(false);
+ GameState.setPrehistoricSeenFlyer2(false);
+ GameState.setPrehistoricSeenBridgeZoom(false);
+ GameState.setPrehistoricBreakerThrown(false);
+ GameState.setScoringGoToPrehistoric(true);
+ GameState.setTSAState(kPlayerWentToPrehistoric);
+ break;
+ case kPlayerOnWayToNorad:
+ _vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth);
+ GameState.setNoradSeenTimeStream(false);
+ GameState.setNoradGassed(true);
+ GameState.setNoradFillingStationOn(false);
+ GameState.setNoradN22MessagePlayed(false);
+ GameState.setNoradPlayedGlobeGame(false);
+ GameState.setNoradBeatRobotWithClaw(false);
+ GameState.setNoradBeatRobotWithDoor(false);
+ GameState.setNoradRetScanGood(false);
+ GameState.setNoradWaitingForLaser(false);
+ GameState.setNoradSubRoomPressure(9);
+ GameState.setNoradSubPrepState(kSubNotPrepped);
+ break;
+ case kPlayerOnWayToMars:
+ _vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth);
+ GameState.setMarsSeenTimeStream(false);
+ GameState.setMarsHeardUpperPodMessage(false);
+ GameState.setMarsRobotThrownPlayer(false);
+ GameState.setMarsHeardCheckInMessage(false);
+ GameState.setMarsPodAtUpperPlatform(false);
+ GameState.setMarsSeenThermalScan(false);
+ GameState.setMarsArrivedBelow(false);
+ GameState.setMarsSeenRobotAtReactor(false);
+ GameState.setMarsAvoidedReactorRobot(false);
+ GameState.setMarsLockFrozen(false);
+ GameState.setMarsLockBroken(false);
+ GameState.setMarsSecurityDown(false);
+ GameState.setMarsAirlockOpen(false);
+ GameState.setMarsReadyForShuttleTransport(false);
+ GameState.setMarsFinishedCanyonChase(false);
+ GameState.setMarsThreadedMaze(false);
+ break;
+ case kPlayerOnWayToWSC:
+ _vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest);
+ GameState.setWSCSeenTimeStream(false);
+ GameState.setWSCPoisoned(false);
+ GameState.setWSCAnsweredAboutDart(false);
+ GameState.setWSCRemovedDart(false);
+ GameState.setWSCAnalyzerOn(false);
+ GameState.setWSCDartInAnalyzer(false);
+ GameState.setWSCAnalyzedDart(false);
+ GameState.setWSCPickedUpAntidote(false);
+ GameState.setWSCSawMorph(false);
+ GameState.setWSCDesignedAntidote(false);
+ GameState.setWSCOfficeMessagesOpen(false);
+ GameState.setWSCSeenNerd(false);
+ GameState.setWSCHeardPage1(false);
+ GameState.setWSCHeardPage2(false);
+ GameState.setWSCHeardCheckIn(false);
+ GameState.setWSCDidPlasmaDodge(false);
+ GameState.setWSCSeenSinclairLecture(false);
+ GameState.setWSCBeenAtWSC93(false);
+ GameState.setWSCCatwalkDark(false);
+ GameState.setWSCRobotDead(false);
+ GameState.setWSCRobotGone(false);
+ break;
+ };
+ break;
+ case kTSA37TimeJumpToPegasus:
+ if (g_energyMonitor)
+ g_energyMonitor->stopEnergyDraining();
+
+ switch (GameState.getTSAState()) {
+ case kPlayerWentToPrehistoric:
+ arriveFromPrehistoric();
+ break;
+ case kPlayerOnWayToNorad:
+ arriveFromNorad();
+ break;
+ case kPlayerOnWayToMars:
+ arriveFromMars();
+ break;
+ case kPlayerOnWayToWSC:
+ arriveFromWSC();
+ break;
+ default:
+ break;
+ }
+ break;
+ case kTSA37DownloadToOpMemReview:
+ switch (GameState.getTSAState()) {
+ case kPlayerOnWayToNorad:
+ g_opticalChip->playOpMemMovie(kPoseidonSpotID);
+ break;
+ case kPlayerOnWayToMars:
+ g_opticalChip->playOpMemMovie(kAriesSpotID);
+ break;
+ case kPlayerOnWayToWSC:
+ g_opticalChip->playOpMemMovie(kMercurySpotID);
+ break;
+ }
+
+ if (GameState.allTimeZonesFinished()) {
+ requestExtraSequence(kTSA37OpMemReviewToAllClear, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37AllClearToCongratulations, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37Congratulations, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37CongratulationsToExit, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ requestExtraSequence(kTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kTSA37RecallToDownload:
+ case kTSA37ReviewRequiredToExit:
+ GameState.setTSAState(kTSAPlayerGotHistoricalLog);
+ initializePegasusButtons(kPegasusUnresolved);
+ break;
+ case kTSA37ZoomToMainMenu:
+ case kTSA37HorseToColonel2:
+ case kTSA37DownloadToMainMenu:
+ case kTSA37OpMemReviewToMainMenu:
+ case kTSA37AI4ToMainMenu:
+ GameState.setTSAState(kPlayerLockedInPegasus);
+ showMainJumpMenu();
+ makeContinuePoint();
+ break;
+ case kTSA37JumpToNoradMenu:
+ setCurrentActivation(kActivationJumpToNorad);
+ break;
+ case kTSA37JumpToMarsMenu:
+ setCurrentActivation(kActivationJumpToMars);
+ break;
+ case kTSA37JumpToWSCMenu:
+ setCurrentActivation(kActivationJumpToWSC);
+ break;
+ case kTSA37CancelNorad:
+ case kTSA37CancelMars:
+ case kTSA37CancelWSC:
+ showMainJumpMenu();
+ break;
+ case kTSA37CongratulationsToExit:
+ GameState.setTSAState(kPlayerFinishedWithTSA);
+ initializePegasusButtons(true);
+ break;
+ }
+ }
+
+ g_AIArea->checkMiddleArea();
+}
+
+void FullTSA::arriveFromPrehistoric() {
+ if (_vm->playerHasItemID(kHistoricalLog)) {
+ GameState.setScoringFinishedPrehistoric();
+ requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37DownloadToColonel1, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37Colonel1, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37Colonel1ToReviewRequired, 0, kFilterNoInput);
+ requestExtraSequence(kTSA37ReviewRequiredToExit, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ // Make sure rip timer is going...
+ startExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+void FullTSA::arriveFromNorad() {
+ requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput);
+
+ if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) {
+ GameState.setScoringFinishedNorad();
+ requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+void FullTSA::arriveFromMars() {
+ requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput);
+
+ if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) {
+ GameState.setScoringFinishedMars();
+ requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+void FullTSA::arriveFromWSC() {
+ requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput);
+
+ if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) {
+ GameState.setScoringFinishedWSC();
+ requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+void FullTSA::initializePegasusButtons(bool resolved) {
+ if (resolved) {
+ _sprite1.addPICTResourceFrame(kResolvedPICTID, false, 0, 0);
+ _sprite1.moveElementTo(kResolvedLeft, kResolvedTop);
+ } else {
+ _sprite1.addPICTResourceFrame(kUnresolvedPICTID, false, 0, 0);
+ _sprite1.moveElementTo(kUnresolvedLeft, kUnresolvedTop);
+ }
+
+ _sprite1.setCurrentFrameIndex(0);
+ _sprite1.show();
+
+ _sprite2.addPICTResourceFrame(kExitPICTID, false, kExitLeft - kExitHilitedLeft, kExitTop - kExitHilitedTop);
+ _sprite2.addPICTResourceFrame(kExitHilitedPICTID, false, 0, 0);
+ _sprite2.moveElementTo(kExitHilitedLeft, kExitHilitedTop);
+ setCurrentActivation(kActivationReadyToExit);
+ _sprite2.setCurrentFrameIndex(0);
+ _sprite2.show();
+}
+
+Hotspot *FullTSA::getItemScreenSpot(Item *item, DisplayElement *element) {
+ switch (item->getObjectID()) {
+ case kJourneymanKey:
+ return _vm->getAllHotspots().findHotspotByID(kTSA22EastKeySpotID);
+ break;
+ case kPegasusBiochip:
+ return _vm->getAllHotspots().findHotspotByID(kTSA23WestChipsSpotID);
+ break;
+ }
+
+ return Neighborhood::getItemScreenSpot(item, element);
+}
+
+void FullTSA::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+
+ switch (item->getObjectID()) {
+ case kKeyCard:
+ if (dropSpot->getObjectID() == kTSAGTCardDropSpotID)
+ startExtraSequence(kTSAGTCardSwipe, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kHistoricalLog:
+ if (dropSpot->getObjectID() == kTSA0BNorthHistLogSpotID) {
+ requestExtraSequence(kTSA0BNorthHistLogCloseWithLog, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BNorthTurnRight, 0, kFilterNoInput);
+ requestExtraSequence(kTSA0BEastZoomIn, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kTSA0BComparisonStartup, kExtraCompletedFlag, kFilterNoInput);
+ GameState.setScoringPutLogInReader(true);
+ }
+ break;
+ }
+}
+
+uint FullTSA::getHistoricalLogIndex() {
+ uint index;
+
+ if (GameState.getTSASeenNoradNormal() && GameState.getTSASeenNoradAltered())
+ index = 8;
+ else
+ index = 0;
+
+ if (GameState.getTSASeenMarsNormal() && GameState.getTSASeenMarsAltered())
+ index += 4;
+
+ if (GameState.getTSASeenCaldoriaNormal() && GameState.getTSASeenCaldoriaAltered())
+ index += 2;
+
+ if (GameState.getTSASeenWSCNormal() && GameState.getTSASeenWSCAltered())
+ index += 1;
+
+ return index;
+}
+
+void FullTSA::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) {
+ case MakeRoomView(kTSA0B, kEast):
+ if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning() && GameState.getT0BMonitorMode() == kMonitorNeutral) {
+ switch (GameState.getTSAState()) {
+ case kTSAPlayerInstalledHistoricalLog:
+ case kTSABossSawHistoricalLog:
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ if (cursorSpot) {
+ switch (cursorSpot->getObjectID()) {
+ case kTSA0BEastCompareNoradSpotID:
+ _sprite1.setCurrentFrameIndex(0);
+ _sprite2.setCurrentFrameIndex(0);
+ _sprite1.show();
+ _sprite2.show();
+ break;
+ case kTSA0BEastCompareMarsSpotID:
+ _sprite1.setCurrentFrameIndex(1);
+ _sprite2.setCurrentFrameIndex(1);
+ _sprite1.show();
+ _sprite2.show();
+ break;
+ case kTSA0BEastCompareCaldoriaSpotID:
+ _sprite1.setCurrentFrameIndex(2);
+ _sprite2.setCurrentFrameIndex(2);
+ _sprite1.show();
+ _sprite2.show();
+ break;
+ case kTSA0BEastCompareWSCSpotID:
+ _sprite1.setCurrentFrameIndex(3);
+ _sprite2.setCurrentFrameIndex(3);
+ _sprite1.show();
+ _sprite2.show();
+ break;
+ default:
+ _sprite1.hide();
+ _sprite2.hide();
+ break;
+ }
+ } else {
+ _sprite1.hide();
+ _sprite2.hide();
+ }
+ break;
+ }
+ }
+ break;
+ case MakeRoomView(kTSA0B, kNorth):
+ if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning()) {
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ case kRobotsAtFrontDoor:
+ case kRobotsAtReadyRoom:
+ if (cursorSpot) {
+ switch (cursorSpot->getObjectID()) {
+ case kTSA0BNorthRobotsToCommandCenterSpotID:
+ _sprite1.setCurrentFrameIndex(kRedirectionCCRolloverSprite);
+ _sprite1.show();
+ break;
+ case kTSA0BNorthRobotsToReadyRoomSpotID:
+ _sprite1.setCurrentFrameIndex(kRedirectionRRRolloverSprite);
+ _sprite1.show();
+ break;
+ case kTSA0BNorthRobotsToFrontDoorSpotID:
+ _sprite1.setCurrentFrameIndex(kRedirectionFDRolloverSprite);
+ _sprite1.show();
+ break;
+ default:
+ _sprite1.hide();
+ break;
+ }
+ } else {
+ _sprite1.hide();
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ Neighborhood::handleInput(input, cursorSpot);
+}
+
+void FullTSA::releaseSprites() {
+ _sprite1.hide();
+ _sprite2.hide();
+ _sprite3.hide();
+ _sprite1.discardFrames();
+ _sprite2.discardFrames();
+ _sprite3.discardFrames();
+}
+
+bool FullTSA::canSolve() {
+ return GameState.getCurrentRoomAndView() == MakeRoomView(kTSA0B, kNorth) &&
+ GameState.getTSA0BZoomedIn() &&
+ (GameState.getTSAState() == kRobotsAtCommandCenter ||
+ GameState.getTSAState() == kRobotsAtFrontDoor ||
+ GameState.getTSAState() == kRobotsAtReadyRoom);
+}
+
+void FullTSA::doSolve() {
+ // REROUTING ROBOTS
+
+ _sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite);
+ _sprite1.show();
+ _vm->delayShell(1, 2);
+ _sprite1.hide();
+
+ switch (GameState.getTSAState()) {
+ case kRobotsAtCommandCenter:
+ GameState.setTSAState(kRobotsAtFrontDoor);
+ _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite);
+ startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kRobotsAtFrontDoor:
+ // Nothing
+ break;
+ case kRobotsAtReadyRoom:
+ GameState.setTSAState(kRobotsAtFrontDoor);
+ _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite);
+ startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+}
+
+void FullTSA::updateCursor(const Common::Point where, const Hotspot *cursorSpot) {
+ if (cursorSpot) {
+ switch (cursorSpot->getObjectID()) {
+ case kTSA0BEastMonitorSpotID:
+ case kTSA0BNorthMonitorSpotID:
+ case kTSA0BWestMonitorSpotID:
+ case kTSA22EastMonitorSpotID:
+ case kTSA23WestMonitorSpotID:
+ _vm->_cursor->setCurrentFrameIndex(1);
+ return;
+ case kTSA0BEastMonitorOutSpotID:
+ case kTSA0BNorthMonitorOutSpotID:
+ case kTSA0BWestMonitorOutSpotID:
+ _vm->_cursor->setCurrentFrameIndex(2);
+ return;
+ }
+ }
+
+ Neighborhood::updateCursor(where, cursorSpot);
+}
+
+Common::String FullTSA::getNavMovieName() {
+ return "Images/TSA/Full TSA.movie";
+}
+
+Common::String FullTSA::getSoundSpotsName() {
+ return "Sounds/TSA/TSA Spots";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.h b/engines/pegasus/neighborhood/tsa/fulltsa.h
new file mode 100644
index 0000000000..a646d57e6c
--- /dev/null
+++ b/engines/pegasus/neighborhood/tsa/fulltsa.h
@@ -0,0 +1,159 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H
+#define PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H
+
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+class RipTimer : public IdlerAnimation {
+public:
+ RipTimer(const DisplayElementID id) : IdlerAnimation(id) {}
+ virtual ~RipTimer() {}
+
+ void initImage();
+ void releaseImage();
+
+ void draw(const Common::Rect &);
+
+protected:
+ void timeChanged(const TimeValue);
+
+ CoordType _middle;
+ Surface _timerImage;
+};
+
+// Room IDs.
+
+static const RoomID kTSA00 = 0;
+static const RoomID kTSA22Red = 28;
+static const RoomID kTSA37 = 42;
+
+class FullTSA : public Neighborhood {
+public:
+ FullTSA(InputHandler *, PegasusEngine *);
+ virtual ~FullTSA() {}
+
+ virtual void init();
+
+ void start();
+
+ virtual uint16 getDateResID() const;
+
+ void flushGameState();
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+ bool canSolve();
+ void doSolve();
+
+ void updateCursor(const Common::Point, const Hotspot *);
+
+protected:
+ enum {
+ kTSAPrivateLogReaderOpenFlag,
+ kTSAPrivateKeyVaultOpenFlag,
+ kTSAPrivateChipVaultOpenFlag,
+ kTSAPrivatePlayingLeftComparisonFlag,
+ kTSAPrivatePlayingRightComparisonFlag,
+ kTSAPrivateSeenRobotWarningFlag,
+ kNumTSAPrivateFlags
+ };
+
+ Common::String getBriefingMovie();
+ Common::String getEnvScanMovie();
+ uint getNumHints();
+ Common::String getHintMovie(uint);
+ void loadAmbientLoops();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant);
+ void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *spot);
+ virtual void activateHotspots();
+ void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &);
+ void dropItemIntoRoom(Item *, Hotspot *);
+ void downButton(const Input &);
+ void startDoorOpenMovie(const TimeValue, const TimeValue);
+ TimeValue getViewTime(const RoomID, const DirectionConstant);
+ void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &);
+ void turnTo(const DirectionConstant);
+ CanMoveForwardReason canMoveForward(ExitTable::Entry &);
+ CanOpenDoorReason canOpenDoor(DoorTable::Entry &);
+ void bumpIntoWall();
+ void initializeTBPMonitor(const int, const ExtraID);
+ void playTBPMonitor();
+ void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &);
+ Hotspot *getItemScreenSpot(Item *, DisplayElement *);
+ void openDoor();
+ void turnRight();
+ void turnLeft();
+ void closeDoorOffScreen(const RoomID, const DirectionConstant);
+ void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionInput);
+ void handleInput(const Input &, const Hotspot *);
+ void arriveAtTSA25Red();
+ void startUpComparisonMonitor();
+ void shutDownComparisonMonitor();
+ void initializeComparisonMonitor(const int, const ExtraID);
+ void playLeftComparison();
+ void playRightComparison();
+ void startRobotGame();
+ void setOffRipAlarm();
+ uint getHistoricalLogIndex();
+ void startUpRobotMonitor();
+ void shutDownRobotMonitor();
+ void pickedUpItem(Item *item);
+ void arriveFromPrehistoric();
+
+ void arriveFromNorad();
+ void arriveFromMars();
+ void arriveFromWSC();
+
+ InputBits getInputFilter();
+ void arriveAt(const RoomID, const DirectionConstant);
+ void initializePegasusButtons(bool);
+ void releaseSprites();
+ void showMainJumpMenu();
+ void arriveAtTSA37();
+ void receiveNotification(Notification *, const NotificationFlags);
+ void checkRobotLocations(const RoomID, const DirectionConstant);
+ void getExtraEntry(const uint32, ExtraTable::Entry &);
+
+ Sprite _sprite1, _sprite2, _sprite3;
+ FuseFunction _utilityFuse;
+ RipTimer _ripTimer;
+
+ FlagsArray<byte, kNumTSAPrivateFlags> _privateFlags;
+
+ Common::String getNavMovieName();
+ Common::String getSoundSpotsName();
+
+ void dieUncreatedInTSA();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.cpp b/engines/pegasus/neighborhood/tsa/tinytsa.cpp
new file mode 100644
index 0000000000..4f109620c1
--- /dev/null
+++ b/engines/pegasus/neighborhood/tsa/tinytsa.cpp
@@ -0,0 +1,453 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/aichip.h"
+#include "pegasus/items/biochips/opticalchip.h"
+#include "pegasus/neighborhood/mars/constants.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/tsa/tinytsa.h"
+#include "pegasus/neighborhood/wsc/wsc.h"
+
+namespace Pegasus {
+
+static const int16 kCompassShift = 30;
+
+static const TimeScale kTinyTSAMovieScale = 600;
+static const TimeScale kTinyTSAFramesPerSecond = 15;
+static const TimeScale kTinyTSAFrameDuration = 40;
+
+// Alternate IDs.
+static const AlternateID kAltTinyTSANormal = 0;
+
+// Hot Spot Activation IDs.
+static const HotSpotActivationID kActivationTinyTSAJumpToNorad = 1;
+static const HotSpotActivationID kActivationTinyTSAJumpToMars = 2;
+static const HotSpotActivationID kActivationTinyTSAJumpToWSC = 3;
+static const HotSpotActivationID kActivationTinyTSAReadyForJumpMenu = 4;
+static const HotSpotActivationID kActivationTinyTSAMainJumpMenu = 5;
+
+// Hot Spot IDs.
+static const HotSpotID kTinyTSA37NorthJumpToNoradSpotID = 5000;
+static const HotSpotID kTinyTSA37NorthCancelNoradSpotID = 5001;
+static const HotSpotID kTinyTSA37NorthJumpToMarsSpotID = 5002;
+static const HotSpotID kTinyTSA37NorthCancelMarsSpotID = 5003;
+static const HotSpotID kTinyTSA37NorthJumpToWSCSpotID = 5004;
+static const HotSpotID kTinyTSA37NorthCancelWSCSpotID = 5005;
+static const HotSpotID kTinyTSA37NorthJumpMenuSpotID = 5006;
+static const HotSpotID kTinyTSA37NorthNoradMenuSpotID = 5007;
+static const HotSpotID kTinyTSA37NorthMarsMenuSpotID = 5008;
+static const HotSpotID kTinyTSA37NorthWSCMenuSpotID = 5009;
+
+// Extra sequence IDs.
+static const ExtraID kTinyTSA37PegasusDepart = 0;
+static const ExtraID kTinyTSA37TimeJumpToPegasus = 1;
+static const ExtraID kTinyTSA37RecallToDownload = 2;
+static const ExtraID kTinyTSA37ExitHilited = 3;
+static const ExtraID kTinyTSA37ExitToHorse = 4;
+static const ExtraID kTinyTSA37JumpMenu000 = 5;
+static const ExtraID kTinyTSA37JumpMenu001 = 6;
+static const ExtraID kTinyTSA37JumpMenu010 = 7;
+static const ExtraID kTinyTSA37JumpMenu011 = 8;
+static const ExtraID kTinyTSA37JumpMenu100 = 9;
+static const ExtraID kTinyTSA37JumpMenu101 = 10;
+static const ExtraID kTinyTSA37JumpMenu110 = 11;
+static const ExtraID kTinyTSA37JumpMenu111 = 12;
+static const ExtraID kTinyTSA37JumpToWSCMenu = 13;
+static const ExtraID kTinyTSA37CancelWSC = 14;
+static const ExtraID kTinyTSA37JumpToWSC = 15;
+static const ExtraID kTinyTSA37WSCToAI5 = 16;
+static const ExtraID kTinyTSA37PegasusAI5 = 17;
+static const ExtraID kTinyTSA37AI5ToWSC = 18;
+static const ExtraID kTinyTSA37WSCToDepart = 19;
+static const ExtraID kTinyTSA37JumpToMarsMenu = 20;
+static const ExtraID kTinyTSA37CancelMars = 21;
+static const ExtraID kTinyTSA37JumpToMars = 22;
+static const ExtraID kTinyTSA37MarsToAI6 = 23;
+static const ExtraID kTinyTSA37PegasusAI6 = 24;
+static const ExtraID kTinyTSA37AI6ToMars = 25;
+static const ExtraID kTinyTSA37MarsToDepart = 26;
+static const ExtraID kTinyTSA37JumpToNoradMenu = 27;
+static const ExtraID kTinyTSA37CancelNorad = 28;
+static const ExtraID kTinyTSA37JumpToNorad = 29;
+static const ExtraID kTinyTSA37NoradToAI7 = 30;
+static const ExtraID kTinyTSA37PegasusAI7 = 31;
+static const ExtraID kTinyTSA37AI7ToNorad = 32;
+static const ExtraID kTinyTSA37NoradToDepart = 33;
+static const ExtraID kTinyTSA37EnvironmentalScan = 34;
+static const ExtraID kTinyTSA37DownloadToMainMenu = 35;
+static const ExtraID kTinyTSA37DownloadToOpMemReview = 36;
+static const ExtraID kTinyTSA37OpMemReviewToMainMenu = 37;
+
+TinyTSA::TinyTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Tiny TSA", kTinyTSAID) {
+}
+
+void TinyTSA::start() {
+ g_energyMonitor->stopEnergyDraining();
+ Neighborhood::start();
+}
+
+Common::String TinyTSA::getBriefingMovie() {
+ Common::String movieName = Neighborhood::getBriefingMovie();
+
+ if (movieName.empty()) {
+ switch (getCurrentActivation()) {
+ case kActivationTinyTSAJumpToNorad:
+ g_AIChip->showBriefingClicked();
+ startExtraSequenceSync(kTinyTSA37PegasusAI7, kHintInterruption);
+ startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput);
+ g_AIChip->clearClicked();
+ movieName = "";
+ break;
+ case kActivationTinyTSAJumpToMars:
+ g_AIChip->showBriefingClicked();
+ startExtraSequenceSync(kTinyTSA37PegasusAI6, kHintInterruption);
+ startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput);
+ g_AIChip->clearClicked();
+ movieName = "";
+ break;
+ case kActivationTinyTSAJumpToWSC:
+ g_AIChip->showBriefingClicked();
+ startExtraSequenceSync(kTinyTSA37PegasusAI5, kHintInterruption);
+ startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput);
+ g_AIChip->clearClicked();
+ movieName = "";
+ break;
+ default:
+ movieName = "Images/AI/TSA/XT04";
+ break;
+ }
+ }
+
+ return movieName;
+}
+
+Common::String TinyTSA::getEnvScanMovie() {
+ Common::String movieName = Neighborhood::getEnvScanMovie();
+
+ if (movieName.empty()) {
+ g_AIChip->showEnvScanClicked();
+ startExtraSequenceSync(kTinyTSA37EnvironmentalScan, kHintInterruption);
+
+ switch (getCurrentActivation()) {
+ case kActivationTinyTSAJumpToNorad:
+ startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput);
+ showExtraView(kTinyTSA37JumpToNoradMenu);
+ break;
+ case kActivationTinyTSAJumpToMars:
+ startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput);
+ showExtraView(kTinyTSA37JumpToMarsMenu);
+ break;
+ case kActivationTinyTSAJumpToWSC:
+ startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput);
+ showExtraView(kTinyTSA37JumpToWSCMenu);
+ break;
+ default:
+ showMainJumpMenu();
+ break;
+ }
+
+ g_AIChip->clearClicked();
+ }
+
+ return movieName;
+}
+
+void TinyTSA::loadAmbientLoops() {
+ loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
+}
+
+int16 TinyTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
+ return Neighborhood::getStaticCompassAngle(room, dir) - kCompassShift;
+}
+
+uint16 TinyTSA::getDateResID() const {
+ return kDate2318ID;
+}
+
+InputBits TinyTSA::getInputFilter() {
+ // Can't move forward...
+ return Neighborhood::getInputFilter() & ~(kFilterUpButton | kFilterUpAuto);
+}
+
+void TinyTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+ if (clickedSpot) {
+ switch (clickedSpot->getObjectID()) {
+ case kTinyTSA37NorthJumpMenuSpotID:
+ // This hotspot isn't accessable from Tiny TSA
+ warning("jump menu spot");
+ return;
+ case kTinyTSA37NorthJumpToNoradSpotID:
+ GameState.setTSAState(kPlayerOnWayToNorad);
+ requestExtraSequence(kTinyTSA37JumpToNorad, 0, kFilterNoInput);
+ if (!GameState.getBeenToNorad()) {
+ requestExtraSequence(kTinyTSA37NoradToAI7, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37PegasusAI7, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37AI7ToNorad, 0, kFilterNoInput);
+ GameState.setBeenToNorad(true);
+ }
+
+ requestExtraSequence(kTinyTSA37NoradToDepart, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput);
+ return;
+ case kTinyTSA37NorthJumpToMarsSpotID:
+ GameState.setTSAState(kPlayerOnWayToMars);
+ requestExtraSequence(kTinyTSA37JumpToMars, 0, kFilterNoInput);
+ if (!GameState.getBeenToMars()) {
+ requestExtraSequence(kTinyTSA37MarsToAI6, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37PegasusAI6, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37AI6ToMars, 0, kFilterNoInput);
+ GameState.setBeenToMars(true);
+ }
+
+ requestExtraSequence(kTinyTSA37MarsToDepart, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput);
+ return;
+ case kTinyTSA37NorthJumpToWSCSpotID:
+ GameState.setTSAState(kPlayerOnWayToWSC);
+ requestExtraSequence(kTinyTSA37JumpToWSC, 0, kFilterNoInput);
+ if (!GameState.getBeenToWSC()) {
+ requestExtraSequence(kTinyTSA37WSCToAI5, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37PegasusAI5, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37AI5ToWSC, 0, kFilterNoInput);
+ GameState.setBeenToWSC(true);
+ }
+
+ requestExtraSequence(kTinyTSA37WSCToDepart, 0, kFilterNoInput);
+ requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput);
+ return;
+ }
+ }
+
+ Neighborhood::clickInHotspot(input, clickedSpot);
+}
+
+void TinyTSA::showMainJumpMenu() {
+ ExtraID jumpMenuView = kTinyTSA37JumpMenu000;
+
+ if (GameState.getNoradFinished())
+ jumpMenuView += 4;
+ if (GameState.getMarsFinished())
+ jumpMenuView += 2;
+ if (GameState.getWSCFinished())
+ jumpMenuView += 1;
+
+ showExtraView(jumpMenuView);
+ setCurrentActivation(kActivationTinyTSAMainJumpMenu);
+}
+
+void TinyTSA::checkContinuePoint(const RoomID, const DirectionConstant) {
+ makeContinuePoint();
+}
+
+void TinyTSA::arriveAt(const RoomID room, const DirectionConstant direction) {
+ Neighborhood::arriveAt(room, direction);
+
+ switch (GameState.getTSAState()) {
+ case kPlayerOnWayToNorad:
+ case kPlayerOnWayToMars:
+ case kPlayerOnWayToWSC:
+ startExtraSequence(kTinyTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kPlayerLockedInPegasus:
+ showMainJumpMenu();
+ break;
+ }
+}
+
+void TinyTSA::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ ExtraID lastExtra = _lastExtra;
+
+ Neighborhood::receiveNotification(notification, flags);
+
+ if ((flags & kExtraCompletedFlag) != 0) {
+ // Only allow input if we're not in the middle of series of queue requests.
+ if (actionQueueEmpty())
+ _interruptionFilter = kFilterAllInput;
+
+ switch (lastExtra) {
+ case kTinyTSA37PegasusDepart:
+ _vm->setLastEnergyValue(kFullEnergy);
+
+ switch (GameState.getTSAState()) {
+ case kPlayerOnWayToNorad:
+ _vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth);
+ GameState.setNoradSeenTimeStream(false);
+ GameState.setNoradGassed(true);
+ GameState.setNoradFillingStationOn(false);
+ GameState.setNoradN22MessagePlayed(false);
+ GameState.setNoradPlayedGlobeGame(false);
+ GameState.setNoradBeatRobotWithClaw(false);
+ GameState.setNoradBeatRobotWithDoor(false);
+ GameState.setNoradRetScanGood(false);
+ GameState.setNoradWaitingForLaser(false);
+ GameState.setNoradSubRoomPressure(9);
+ GameState.setNoradSubPrepState(kSubNotPrepped);
+ break;
+ case kPlayerOnWayToMars:
+ _vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth);
+ GameState.setMarsSeenTimeStream(false);
+ GameState.setMarsHeardUpperPodMessage(false);
+ GameState.setMarsRobotThrownPlayer(false);
+ GameState.setMarsHeardCheckInMessage(false);
+ GameState.setMarsPodAtUpperPlatform(false);
+ GameState.setMarsSeenThermalScan(false);
+ GameState.setMarsArrivedBelow(false);
+ GameState.setMarsSeenRobotAtReactor(false);
+ GameState.setMarsAvoidedReactorRobot(false);
+ GameState.setMarsLockFrozen(false);
+ GameState.setMarsLockBroken(false);
+ GameState.setMarsSecurityDown(false);
+ GameState.setMarsAirlockOpen(false);
+ GameState.setMarsReadyForShuttleTransport(false);
+ GameState.setMarsFinishedCanyonChase(false);
+ GameState.setMarsThreadedMaze(false);
+ break;
+ case kPlayerOnWayToWSC:
+ _vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest);
+ GameState.setWSCSeenTimeStream(false);
+ GameState.setWSCPoisoned(false);
+ GameState.setWSCAnsweredAboutDart(false);
+ GameState.setWSCDartInAnalyzer(false);
+ GameState.setWSCRemovedDart(false);
+ GameState.setWSCAnalyzerOn(false);
+ GameState.setWSCAnalyzedDart(false);
+ GameState.setWSCPickedUpAntidote(false);
+ GameState.setWSCSawMorph(false);
+ GameState.setWSCDesignedAntidote(false);
+ GameState.setWSCOfficeMessagesOpen(false);
+ GameState.setWSCSeenNerd(false);
+ GameState.setWSCHeardPage1(false);
+ GameState.setWSCHeardPage2(false);
+ GameState.setWSCHeardCheckIn(false);
+ GameState.setWSCDidPlasmaDodge(false);
+ GameState.setWSCSeenSinclairLecture(false);
+ GameState.setWSCBeenAtWSC93(false);
+ GameState.setWSCCatwalkDark(false);
+ GameState.setWSCRobotDead(false);
+ GameState.setWSCRobotGone(false);
+ break;
+ };
+ break;
+ case kTinyTSA37TimeJumpToPegasus:
+ if (g_energyMonitor)
+ g_energyMonitor->stopEnergyDraining();
+
+ switch (GameState.getTSAState()) {
+ case kPlayerOnWayToNorad:
+ arriveFromNorad();
+ break;
+ case kPlayerOnWayToMars:
+ arriveFromMars();
+ break;
+ case kPlayerOnWayToWSC:
+ arriveFromWSC();
+ break;
+ default:
+ break;
+ }
+ break;
+ case kTinyTSA37DownloadToOpMemReview:
+ switch (GameState.getTSAState()) {
+ case kPlayerOnWayToNorad:
+ g_opticalChip->playOpMemMovie(kPoseidonSpotID);
+ break;
+ case kPlayerOnWayToMars:
+ g_opticalChip->playOpMemMovie(kAriesSpotID);
+ break;
+ case kPlayerOnWayToWSC:
+ g_opticalChip->playOpMemMovie(kMercurySpotID);
+ break;
+ }
+
+ requestExtraSequence(kTinyTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTinyTSA37DownloadToMainMenu:
+ case kTinyTSA37OpMemReviewToMainMenu:
+ GameState.setTSAState(kPlayerLockedInPegasus);
+ showMainJumpMenu();
+ makeContinuePoint();
+ break;
+ case kTinyTSA37JumpToNoradMenu:
+ setCurrentActivation(kActivationTinyTSAJumpToNorad);
+ break;
+ case kTinyTSA37JumpToMarsMenu:
+ setCurrentActivation(kActivationTinyTSAJumpToMars);
+ break;
+ case kTinyTSA37JumpToWSCMenu:
+ setCurrentActivation(kActivationTinyTSAJumpToWSC);
+ break;
+ case kTinyTSA37CancelNorad:
+ case kTinyTSA37CancelMars:
+ case kTinyTSA37CancelWSC:
+ showMainJumpMenu();
+ break;
+ }
+ }
+
+ g_AIArea->checkMiddleArea();
+}
+
+void TinyTSA::arriveFromNorad() {
+ requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput);
+
+ if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) {
+ GameState.setScoringFinishedNorad();
+ requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+void TinyTSA::arriveFromMars() {
+ requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput);
+
+ if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) {
+ GameState.setScoringFinishedMars();
+ requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+void TinyTSA::arriveFromWSC() {
+ requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput);
+
+ if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) {
+ GameState.setScoringFinishedWSC();
+ requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+Common::String TinyTSA::getNavMovieName() {
+ return "Images/TSA/Tiny TSA.movie";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.h b/engines/pegasus/neighborhood/tsa/tinytsa.h
new file mode 100644
index 0000000000..2dc234675d
--- /dev/null
+++ b/engines/pegasus/neighborhood/tsa/tinytsa.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H
+#define PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H
+
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+// Room IDs.
+
+static const RoomID kTinyTSA37 = 0;
+
+class TinyTSA : public Neighborhood {
+public:
+ TinyTSA(InputHandler *, PegasusEngine *);
+ virtual ~TinyTSA() {}
+
+ virtual uint16 getDateResID() const;
+
+ void start();
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+protected:
+ Common::String getBriefingMovie();
+ Common::String getEnvScanMovie();
+ void loadAmbientLoops();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant);
+
+ void arriveFromNorad();
+ void arriveFromMars();
+ void arriveFromWSC();
+
+ InputBits getInputFilter();
+ void arriveAt(const RoomID, const DirectionConstant);
+ void showMainJumpMenu();
+ void receiveNotification(Notification *, const NotificationFlags);
+
+ Common::String getNavMovieName();
+ Common::String getSoundSpotsName() { return ""; }
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/turn.cpp b/engines/pegasus/neighborhood/turn.cpp
new file mode 100644
index 0000000000..1157796f55
--- /dev/null
+++ b/engines/pegasus/neighborhood/turn.cpp
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "pegasus/neighborhood/turn.h"
+
+namespace Pegasus {
+
+void TurnTable::loadFromStream(Common::SeekableReadStream *stream) {
+ uint32 count = stream->readUint32BE();
+ _entries.resize(count);
+
+ for (uint32 i = 0; i < count; i++) {
+ _entries[i].room = stream->readUint16BE();
+ _entries[i].direction = stream->readByte();
+ _entries[i].turnDirection = stream->readByte();
+ _entries[i].altCode = stream->readByte();
+ stream->readByte(); // alignment
+ _entries[i].endDirection = stream->readByte();
+ stream->readByte(); // alignment
+ debug(0, "Turn[%d]: %d %d %d %d %d", i, _entries[i].room, _entries[i].direction,
+ _entries[i].turnDirection, _entries[i].altCode, _entries[i].endDirection);
+ }
+}
+
+void TurnTable::clear() {
+ _entries.clear();
+}
+
+TurnTable::Entry TurnTable::findEntry(RoomID room, DirectionConstant direction, TurnDirection turnDirection, AlternateID altCode) {
+ for (uint32 i = 0; i < _entries.size(); i++)
+ if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].turnDirection == turnDirection && _entries[i].altCode == altCode)
+ return _entries[i];
+
+ return Entry();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/turn.h b/engines/pegasus/neighborhood/turn.h
new file mode 100644
index 0000000000..329b03eddb
--- /dev/null
+++ b/engines/pegasus/neighborhood/turn.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_TURN_H
+#define PEGASUS_NEIGHBORHOOD_TURN_H
+
+#include "common/array.h"
+#include "common/endian.h"
+
+#include "pegasus/constants.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+class TurnTable {
+public:
+ TurnTable() {}
+ ~TurnTable() {}
+
+ static uint32 getResTag() { return MKTAG('T', 'u', 'r', 'n'); }
+
+ void loadFromStream(Common::SeekableReadStream *stream);
+ void clear();
+
+ struct Entry {
+ Entry() { endDirection = kNoDirection; }
+ bool isEmpty() { return endDirection == kNoDirection; }
+
+ RoomID room;
+ DirectionConstant direction;
+ TurnDirection turnDirection;
+ AlternateID altCode;
+ DirectionConstant endDirection;
+ };
+
+ Entry findEntry(RoomID room, DirectionConstant direction, TurnDirection turnDirection, AlternateID altCode);
+
+private:
+ Common::Array<Entry> _entries;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/view.cpp b/engines/pegasus/neighborhood/view.cpp
new file mode 100644
index 0000000000..4e46f5374e
--- /dev/null
+++ b/engines/pegasus/neighborhood/view.cpp
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "pegasus/neighborhood/view.h"
+
+namespace Pegasus {
+
+void ViewTable::loadFromStream(Common::SeekableReadStream *stream) {
+ uint32 count = stream->readUint32BE();
+ _entries.resize(count);
+
+ for (uint32 i = 0; i < count; i++) {
+ _entries[i].room = stream->readUint16BE();
+ _entries[i].direction = stream->readByte();
+ _entries[i].altCode = stream->readByte();
+ _entries[i].time = stream->readUint32BE();
+ debug(0, "View[%d]: %d %d %d %d", i, _entries[i].room, _entries[i].direction,
+ _entries[i].altCode, _entries[i].time);
+ }
+}
+
+void ViewTable::clear() {
+ _entries.clear();
+}
+
+ViewTable::Entry ViewTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) {
+ for (uint32 i = 0; i < _entries.size(); i++)
+ if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode)
+ return _entries[i];
+
+ return Entry();
+}
+
+} // End of namespace pegasus
diff --git a/engines/pegasus/neighborhood/view.h b/engines/pegasus/neighborhood/view.h
new file mode 100644
index 0000000000..3397508b61
--- /dev/null
+++ b/engines/pegasus/neighborhood/view.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_VIEW_H
+#define PEGASUS_NEIGHBORHOOD_VIEW_H
+
+#include "common/array.h"
+#include "common/endian.h"
+
+#include "pegasus/constants.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+class ViewTable {
+public:
+ ViewTable() {}
+ ~ViewTable() {}
+
+ static uint32 getResTag() { return MKTAG('V', 'i', 'e', 'w'); }
+
+ void loadFromStream(Common::SeekableReadStream *stream);
+ void clear();
+
+ struct Entry {
+ Entry() { time = 0xffffffff; }
+ bool isEmpty() { return time == 0xffffffff; }
+
+ RoomID room;
+ DirectionConstant direction;
+ AlternateID altCode;
+ TimeValue time;
+ };
+
+ Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode);
+
+private:
+ Common::Array<Entry> _entries;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/wsc/moleculebin.cpp b/engines/pegasus/neighborhood/wsc/moleculebin.cpp
new file mode 100644
index 0000000000..210c0ad313
--- /dev/null
+++ b/engines/pegasus/neighborhood/wsc/moleculebin.cpp
@@ -0,0 +1,127 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/graphics.h"
+#include "pegasus/neighborhood/wsc/moleculebin.h"
+#include "pegasus/neighborhood/wsc/wsc.h"
+
+namespace Pegasus {
+
+static const CoordType kMoleculeBinWidth = 138;
+static const CoordType kMoleculeBinHeight = 128;
+
+static const CoordType kMoleculeWidth = 66;
+static const CoordType kMoleculeHeight = 40;
+
+static const CoordType kMoleculeBinLeft = kNavAreaLeft + 286;
+static const CoordType kMoleculeBinTop = kNavAreaLeft + 96;
+
+// Layouts:
+
+MoleculeBin::MoleculeBin() : DisplayElement(kNoDisplayElement) {
+ _highlightColor = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 102);
+ _selectedMolecule = -1;
+}
+
+void MoleculeBin::initMoleculeBin() {
+ if (!isDisplaying()) {
+ for (int i = 0; i < 6; i++)
+ _binLayout[i] = i;
+
+ resetBin();
+ _binImages.getImageFromPICTFile("Images/World Science Center/Molecules");
+ setDisplayOrder(kWSCMoleculeBinOrder);
+ setBounds(kMoleculeBinLeft, kMoleculeBinTop, kMoleculeBinLeft + kMoleculeBinWidth,
+ kMoleculeBinTop + kMoleculeBinHeight);
+ startDisplaying();
+ show();
+ }
+}
+
+void MoleculeBin::cleanUpMoleculeBin() {
+ if (isDisplaying()) {
+ stopDisplaying();
+ _binImages.deallocateSurface();
+ }
+}
+
+void MoleculeBin::setBinLayout(const uint32 *layout) {
+ for (int i = 0; i < 6; i++)
+ _binLayout[i] = layout[i];
+}
+
+void MoleculeBin::highlightMolecule(const uint32 whichMolecule) {
+ if (!_moleculeFlags.getFlag(whichMolecule)) {
+ _moleculeFlags.setFlag(whichMolecule, true);
+ triggerRedraw();
+ }
+}
+
+bool MoleculeBin::isMoleculeHighlighted(uint32 whichMolecule) {
+ return _moleculeFlags.getFlag(whichMolecule);
+}
+
+void MoleculeBin::selectMolecule(const int whichMolecule) {
+ if (_selectedMolecule != whichMolecule) {
+ _selectedMolecule = whichMolecule;
+ triggerRedraw();
+ }
+}
+
+void MoleculeBin::resetBin() {
+ _moleculeFlags.clearAllFlags();
+ _selectedMolecule = -1;
+ triggerRedraw();
+}
+
+void MoleculeBin::draw(const Common::Rect &) {
+ Common::Rect r1(0, 0, kMoleculeWidth, kMoleculeHeight);
+ Common::Rect r2 = r1;
+
+ for (int i = 0; i < 6; i++) {
+ r1.moveTo(i * (kMoleculeWidth * 2), 0);
+
+ if (_moleculeFlags.getFlag(_binLayout[i]))
+ r1.translate(kMoleculeWidth, 0);
+
+ r2.moveTo((_binLayout[i] & 1) * (kMoleculeWidth + 2) + _bounds.left + 2,
+ (_binLayout[i] >> 1) * (kMoleculeHeight + 2) + _bounds.top + 2);
+
+ _binImages.copyToCurrentPort(r1, r2);
+ }
+
+ if (_selectedMolecule >= 0) {
+ r2.moveTo((_selectedMolecule & 1) * (kMoleculeWidth + 2) + _bounds.left + 2,
+ (_selectedMolecule >> 1) * (kMoleculeHeight + 2) + _bounds.top + 2);
+
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
+
+ screen->frameRect(r2, _highlightColor);
+ r2.grow(1);
+ screen->frameRect(r2, _highlightColor);
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/wsc/moleculebin.h b/engines/pegasus/neighborhood/wsc/moleculebin.h
new file mode 100644
index 0000000000..3de4b5ed2a
--- /dev/null
+++ b/engines/pegasus/neighborhood/wsc/moleculebin.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_WSC_MOLECULEBIN_H
+#define PEGASUS_NEIGHBORHOOD_WSC_MOLECULEBIN_H
+
+#include "pegasus/elements.h"
+#include "pegasus/surface.h"
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+enum {
+ kMolecule1,
+ kMolecule2,
+ kMolecule3,
+ kMolecule4,
+ kMolecule5,
+ kMolecule6
+};
+
+class MoleculeBin : public DisplayElement {
+public:
+ MoleculeBin();
+ virtual ~MoleculeBin() {}
+
+ void initMoleculeBin();
+ void cleanUpMoleculeBin();
+
+ void setBinLayout(const uint32 *);
+
+ void highlightMolecule(const uint32 whichMolecule);
+ void selectMolecule(const int whichMolecule);
+ void resetBin();
+
+ bool isMoleculeHighlighted(uint32);
+
+protected:
+ void draw(const Common::Rect &);
+
+ Surface _binImages;
+ FlagsArray<byte, kMolecule6 + 1> _moleculeFlags;
+ int _selectedMolecule;
+ uint32 _binLayout[6];
+ uint32 _highlightColor;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/wsc/wsc.cpp b/engines/pegasus/neighborhood/wsc/wsc.cpp
new file mode 100644
index 0000000000..f3bf113333
--- /dev/null
+++ b/engines/pegasus/neighborhood/wsc/wsc.cpp
@@ -0,0 +1,2542 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/opticalchip.h"
+#include "pegasus/items/biochips/shieldchip.h"
+#include "pegasus/neighborhood/wsc/wsc.h"
+
+namespace Pegasus {
+
+static const CanMoveForwardReason kCantMoveWatchingDiagnosis = kCantMoveLastReason + 1;
+
+static const CanTurnReason kCantTurnWatchingDiagnosis = kCantTurnLastReason + 1;
+static const CanTurnReason kCantTurnWatchingAnalysis = kCantTurnWatchingDiagnosis + 1;
+static const CanTurnReason kCantTurnInMoleculeGame = kCantTurnWatchingAnalysis + 1;
+
+static const TimeScale kMoleculesMovieScale = 600;
+static const TimeValue kMoleculeLoopTime = 4 * kMoleculesMovieScale;
+static const TimeValue kMoleculeFailTime = 2 * kMoleculesMovieScale;
+
+enum {
+ kMoleculeLoop0Time = 0,
+ kMoleculeFail0Time = kMoleculeLoop0Time + kMoleculeLoopTime,
+ kMoleculeLoop1Time = kMoleculeFail0Time + kMoleculeFailTime,
+ kMoleculeFail1Time = kMoleculeLoop1Time + kMoleculeLoopTime,
+ kMoleculeLoop2Time = kMoleculeFail1Time + kMoleculeFailTime,
+ kMoleculeFail2Time = kMoleculeLoop2Time + kMoleculeLoopTime,
+ kMoleculeLoop3Time = kMoleculeFail2Time + kMoleculeFailTime,
+ kMoleculeFail3Time = kMoleculeLoop3Time + kMoleculeLoopTime,
+ kMoleculeLoop4Time = kMoleculeFail3Time + kMoleculeFailTime,
+ kMoleculeFail4Time = kMoleculeLoop4Time + kMoleculeLoopTime,
+ kMoleculeLoop5Time = kMoleculeFail4Time + kMoleculeFailTime,
+ kMoleculeFail5Time = kMoleculeLoop5Time + kMoleculeLoopTime,
+ kMoleculeLoop6Time = kMoleculeFail5Time + kMoleculeFailTime
+};
+
+static const TimeValue s_moleculeLoopTimes[] = {
+ kMoleculeLoop0Time,
+ kMoleculeLoop1Time,
+ kMoleculeLoop2Time,
+ kMoleculeLoop3Time,
+ kMoleculeLoop4Time,
+ kMoleculeLoop5Time,
+ kMoleculeLoop6Time
+};
+
+static const TimeValue s_moleculeFailTimes[] = {
+ kMoleculeFail0Time,
+ kMoleculeFail1Time,
+ kMoleculeFail2Time,
+ kMoleculeFail3Time,
+ kMoleculeFail4Time,
+ kMoleculeFail5Time
+};
+
+static const int16 kAuditoriumAngleOffset = 5;
+
+static const int kPlasmaEnergyWithShield = kMaxJMPEnergy * 10 / 100;
+static const int kPlasmaEnergyNoShield = kMaxJMPEnergy * 20 / 100;
+
+static const int kTimerEventPlasmaHit = 0;
+static const int kTimerEventPlayerGawkingAtRobot = 1;
+static const int kTimerEventPlayerGawkingAtRobot2 = 2;
+
+static const TimeValue kWSCMolecule1In = 0;
+static const TimeValue kWSCMolecule1Out = 937;
+
+static const TimeValue kWSCMolecule2In = 937;
+static const TimeValue kWSCMolecule2Out = 1864;
+
+static const TimeValue kWSCMolecule3In = 1864;
+static const TimeValue kWSCMolecule3Out = 2790;
+
+static const TimeValue kWSCClick1In = 2790;
+static const TimeValue kWSCClick1Out = 2890;
+
+static const TimeValue kWSCClick2In = 2890;
+static const TimeValue kWSCClick2Out = 3059;
+
+static const TimeValue kWSCClick3In = 3059;
+static const TimeValue kWSCClick3Out = 3156;
+
+static const TimeValue kWSCFlashlightClickIn = 3156;
+static const TimeValue kWSCFlashlightClickOut = 3211;
+
+static const TimeValue kWSCBumpIntoWallIn = 3211;
+static const TimeValue kWSCBumpIntoWallOut = 3514;
+
+static const TimeValue kWSCCantTransportIn = 3514;
+static const TimeValue kWSCCantTransportOut = 7791;
+
+static const TimeValue kHernandezNotHomeIn = 7791;
+static const TimeValue kHernandezNotHomeOut = 10199;
+
+static const TimeValue kWashingtonNotHomeIn = 10199;
+static const TimeValue kWashingtonNotHomeOut = 12649;
+
+static const TimeValue kSullivanNotHomeIn = 12649;
+static const TimeValue kSullivanNotHomeOut = 15031;
+
+static const TimeValue kNakamuraNotHomeIn = 15031;
+static const TimeValue kNakamuraNotHomeOut = 17545;
+
+static const TimeValue kGrailisNotHomeIn = 17545;
+static const TimeValue kGrailisNotHomeOut = 19937;
+
+static const TimeValue kTheriaultNotHomeIn = 19937;
+static const TimeValue kTheriaultNotHomeOut = 22395;
+
+static const TimeValue kGlennerNotHomeIn = 22395;
+static const TimeValue kGlennerNotHomeOut = 24770;
+
+static const TimeValue kSinclairNotHomeIn = 24770;
+static const TimeValue kSinclairNotHomeOut = 27328;
+
+static const TimeValue kWSCLabClosedIn = 27328;
+static const TimeValue kWSCLabClosedOut = 28904;
+
+static const TimeValue kSlidingDoorCloseIn = 28904;
+static const TimeValue kSlidingDoorCloseOut = 29295;
+
+static const TimeValue kSlimyDoorCloseIn = 29295;
+static const TimeValue kSlimyDoorCloseOut = 29788;
+
+static const TimeValue kPaging1In = 29788;
+static const TimeValue kPaging1Out = 32501;
+
+static const TimeValue kPaging2In = 32501;
+static const TimeValue kPaging2Out = 34892;
+
+static const TimeValue kCheckInIn = 34892;
+static const TimeValue kCheckInOut = 37789;
+
+static const TimeValue kDrinkAntidoteIn = 37789;
+static const TimeValue kDrinkAntidoteOut = 39725;
+
+static const TimeScale kWSCMovieScale = 600;
+static const TimeScale kWSCFramesPerSecond = 15;
+static const TimeScale kWSCFrameDuration = 40;
+
+// Alternate IDs.
+static const AlternateID kAltWSCNormal = 0;
+static const AlternateID kAltWSCTookMachineGun = 1;
+static const AlternateID kAltWSCW0ZDoorOpen = 2;
+static const AlternateID kAltWSCPeopleAtW19North = 3;
+
+// Room IDs.
+static const RoomID kWSC02 = 1;
+static const RoomID kWSC03 = 4;
+static const RoomID kWSC04 = 5;
+static const RoomID kWSC06 = 6;
+static const RoomID kWSC07 = 7;
+static const RoomID kWSC08 = 8;
+static const RoomID kWSC09 = 9;
+static const RoomID kWSC10 = 10;
+static const RoomID kWSC11 = 11;
+static const RoomID kWSC13 = 12;
+static const RoomID kWSC14 = 13;
+static const RoomID kWSC15 = 14;
+static const RoomID kWSC16 = 15;
+static const RoomID kWSC17 = 16;
+static const RoomID kWSC18 = 17;
+static const RoomID kWSC19 = 18;
+static const RoomID kWSC20 = 19;
+static const RoomID kWSC21 = 20;
+static const RoomID kWSC22 = 21;
+static const RoomID kWSC23 = 22;
+static const RoomID kWSC24 = 23;
+static const RoomID kWSC25 = 24;
+static const RoomID kWSC26 = 25;
+static const RoomID kWSC27 = 26;
+static const RoomID kWSC28 = 27;
+static const RoomID kWSC29 = 28;
+static const RoomID kWSC31 = 29;
+static const RoomID kWSC32 = 30;
+static const RoomID kWSC33 = 31;
+static const RoomID kWSC34 = 32;
+static const RoomID kWSC35 = 33;
+static const RoomID kWSC36 = 34;
+static const RoomID kWSC37 = 35;
+static const RoomID kWSC38 = 36;
+static const RoomID kWSC39 = 37;
+static const RoomID kWSC40 = 38;
+static const RoomID kWSC41 = 39;
+static const RoomID kWSC42 = 40;
+static const RoomID kWSC43 = 41;
+static const RoomID kWSC44 = 42;
+static const RoomID kWSC45 = 43;
+static const RoomID kWSC46 = 44;
+static const RoomID kWSC47 = 45;
+static const RoomID kWSC48 = 46;
+static const RoomID kWSC49 = 47;
+static const RoomID kWSC50 = 48;
+static const RoomID kWSC52 = 49;
+static const RoomID kWSC53 = 50;
+static const RoomID kWSC54 = 51;
+static const RoomID kWSC55 = 52;
+static const RoomID kWSC56 = 53;
+static const RoomID kWSC57 = 54;
+static const RoomID kWSC58 = 55;
+static const RoomID kWSC60 = 56;
+static const RoomID kWSC60East = 57;
+static const RoomID kWSC60North = 58;
+static const RoomID kWSC61 = 59;
+static const RoomID kWSC61South = 60;
+static const RoomID kWSC61West = 61;
+static const RoomID kWSC63 = 63;
+static const RoomID kWSC64 = 64;
+static const RoomID kWSC65 = 65;
+static const RoomID kWSC65Screen = 66;
+static const RoomID kWSC66 = 67;
+static const RoomID kWSC67 = 68;
+static const RoomID kWSC68 = 69;
+static const RoomID kWSC69 = 70;
+static const RoomID kWSC70 = 71;
+static const RoomID kWSC71 = 72;
+static const RoomID kWSC72 = 73;
+static const RoomID kWSC73 = 74;
+static const RoomID kWSC74 = 75;
+static const RoomID kWSC75 = 76;
+static const RoomID kWSC0Z = 77;
+static const RoomID kWSC76 = 78;
+static const RoomID kWSC77 = 79;
+static const RoomID kWSC78 = 80;
+static const RoomID kWSC79 = 81;
+static const RoomID kWSC80 = 82;
+static const RoomID kWSC81 = 83;
+static const RoomID kWSC82 = 84;
+static const RoomID kWSC83 = 85;
+static const RoomID kWSC84 = 86;
+static const RoomID kWSC85 = 87;
+static const RoomID kWSC86 = 88;
+static const RoomID kWSC87 = 89;
+static const RoomID kWSC88 = 90;
+static const RoomID kWSC89 = 91;
+static const RoomID kWSC90 = 92;
+static const RoomID kWSC91 = 93;
+static const RoomID kWSC92 = 94;
+static const RoomID kWSC93 = 95;
+static const RoomID kWSC94 = 96;
+static const RoomID kWSC95 = 97;
+static const RoomID kWSC96 = 98;
+static const RoomID kWSC97 = 99;
+static const RoomID kWSC98 = 100;
+static const RoomID kWSCDeathRoom = 101;
+
+// Hot Spot Activation IDs.
+static const HotSpotActivationID kActivationZoomedInToAnalyzer = 1;
+static const HotSpotActivationID kActivationShotByRobot = 2;
+static const HotSpotActivationID kActivationWarnedAboutPoison = 3;
+static const HotSpotActivationID kActivationMorphScreenOff = 4;
+static const HotSpotActivationID kActivationReadyForMorph = 5;
+static const HotSpotActivationID kActivationMorphLooping = 6;
+static const HotSpotActivationID kActivationMorphInterrupted = 7;
+static const HotSpotActivationID kActivationW03NorthOff = 8;
+static const HotSpotActivationID kActivationW03NorthReadyForInstructions = 9;
+static const HotSpotActivationID kActivationW03NorthSawInstructions = 10;
+static const HotSpotActivationID kActivationW03NorthInGame = 11;
+static const HotSpotActivationID kActivationReadyForSynthesis = 12;
+static const HotSpotActivationID kActivationSynthesizerLooping = 13;
+static const HotSpotActivationID kActivationReadyForMap = 14;
+static const HotSpotActivationID kActivationSinclairOfficeLocked = 15;
+static const HotSpotActivationID kActivationW58SouthDoorLocked = 16;
+static const HotSpotActivationID kActivationW61SouthOff = 17;
+static const HotSpotActivationID kActivationW61SouthOn = 18;
+static const HotSpotActivationID kActivationW61MessagesOff = 19;
+static const HotSpotActivationID kActivationW61MessagesOn = 20;
+static const HotSpotActivationID kActivationWSCRobotHeadOpen = 21;
+static const HotSpotActivationID kActivationRobotTurning = 22;
+static const HotSpotActivationID kActivationRobotDead = 23;
+static const HotSpotActivationID kActivationRobotGone = 24;
+
+// Hot Spot IDs.
+static const HotSpotID kWSCDropDartSpotID = 5000;
+static const HotSpotID kWSCTurnOnAnalyzerSpotID = 5001;
+static const HotSpotID kWSCAnalyzerScreenSpotID = 5002;
+static const HotSpotID kWSCSpinRobotSpotID = 5003;
+static const HotSpotID kWSC01YesSpotID = 5004;
+static const HotSpotID kWSC01NoSpotID = 5005;
+static const HotSpotID kWSC01AcknowledgeWarningSpotID = 5006;
+static const HotSpotID kWSC02SouthMorphSpotID = 5007;
+static const HotSpotID kWSC02SouthMessagesSpotID = 5008;
+static const HotSpotID kWSC02SouthMorphOutSpotID = 5009;
+static const HotSpotID kWSC02ActivateMorphScreenSpotID = 5010;
+static const HotSpotID kWSC02SouthStartMorphSpotID = 5011;
+static const HotSpotID kWSC02SouthInterruptMorphSpotID = 5012;
+static const HotSpotID kWSC02SouthMorphFinishedSpotID = 5013;
+static const HotSpotID kWSC02SouthTakeArgonSpotID = 5014;
+static const HotSpotID kWSC02SouthMessagesOutSpotID = 5015;
+static const HotSpotID kWSC02SouthTakeNitrogenSpotID = 5016;
+static const HotSpotID kWSC02SouthPlayMessagesSpotID = 5017;
+static const HotSpotID kWSC03NorthActivateScreenSpotID = 5018;
+static const HotSpotID kWSC03NorthBuildMoleculeSpotID = 5019;
+static const HotSpotID kWSC03NorthProceedSpotID = 5020;
+static const HotSpotID kWSC03NorthMolecule1SpotID = 5021;
+static const HotSpotID kWSC03NorthMolecule2SpotID = 5022;
+static const HotSpotID kWSC03NorthMolecule3SpotID = 5023;
+static const HotSpotID kWSC03NorthMolecule4SpotID = 5024;
+static const HotSpotID kWSC03NorthMolecule5SpotID = 5025;
+static const HotSpotID kWSC03NorthMolecule6SpotID = 5026;
+static const HotSpotID kWSC03SouthActivateSynthesizerSpotID = 5027;
+static const HotSpotID kWSC03SouthPickUpAntidoteSpotID = 5028;
+static const HotSpotID kWSC07SouthMapSpotID = 5029;
+static const HotSpotID kW42EastUnlockDoorSpotID = 5030;
+static const HotSpotID kW56NorthMapSpotID = 5031;
+static const HotSpotID kW58SouthPryDoorSpotID = 5032;
+static const HotSpotID kWSC60EastSpotID = 5033;
+static const HotSpotID kWSC60NorthSpotID = 5034;
+static const HotSpotID kWSC60EastOutSpotID = 5035;
+static const HotSpotID kWSC60NorthOutSpotID = 5036;
+static const HotSpotID kWSC61EastSpotID = 5037;
+static const HotSpotID kWSC61SouthSpotID = 5038;
+static const HotSpotID kW61SouthMachineGunSpotID = 5039;
+static const HotSpotID kW61SouthDropMachineGunSpotID = 5040;
+static const HotSpotID kWSC61WestSpotID = 5041;
+static const HotSpotID kWSC61SouthOutSpotID = 5042;
+static const HotSpotID kW61SouthActivateSpotID = 5043;
+static const HotSpotID kW61SmartAlloysSpotID = 5044;
+static const HotSpotID kW61MorphingSpotID = 5045;
+static const HotSpotID kW61TimeBendingSpotID = 5046;
+static const HotSpotID kWSC61WestOutSpotID = 5047;
+static const HotSpotID kW61TurnOnMessagesSpotID = 5048;
+static const HotSpotID kW61WhiteMessageSpotID = 5049;
+static const HotSpotID kW61WalchekMessageSpotID = 5050;
+static const HotSpotID kWSC65SouthScreenSpotID = 5051;
+static const HotSpotID kWSC65SouthScreenOutSpotID = 5052;
+static const HotSpotID kW98RetinalChipSpotID = 5053;
+static const HotSpotID kW98MapChipSpotID = 5054;
+static const HotSpotID kW98OpticalChipSpotID = 5055;
+static const HotSpotID kW98DropArgonSpotID = 5056;
+static const HotSpotID kW98GrabCableSpotID = 5057;
+static const HotSpotID kW98OpenRobotSpotID = 5058;
+static const HotSpotID kW98StunGunSpotID = 5059;
+
+// Extra sequence IDs.
+static const ExtraID kWSCArrivalFromTSA = 0;
+static const ExtraID kWSCShotByRobot = 1;
+static const ExtraID kWSCDartScan1 = 2;
+static const ExtraID kWSCDartScan2 = 3;
+static const ExtraID kWSCDartScanNo = 4;
+static const ExtraID kWSCDartScan3 = 5;
+static const ExtraID kWSCAnalyzerPowerUp = 6;
+static const ExtraID kWSCAnalyzerPowerUpWithDart = 7;
+static const ExtraID kWSCDropDartIntoAnalyzer = 8;
+static const ExtraID kWSCAnalyzeDart = 9;
+static const ExtraID kWSCZoomOutFromAnalyzer = 10;
+static const ExtraID kWSCSpinRobot = 11;
+static const ExtraID kWSC02MorphZoomNoArgon = 12;
+static const ExtraID kWSC02MessagesZoomNoNitrogen = 13;
+static const ExtraID kWSC02ZoomOutNoArgon = 14;
+static const ExtraID kWSC02TurnOnMorphScreen = 15;
+static const ExtraID kWSC02DropToMorphExperiment = 16;
+static const ExtraID kWSC02MorphLoop = 17;
+static const ExtraID kWSC02MorphInterruption = 18;
+static const ExtraID kWSC02MorphFinished = 19;
+static const ExtraID kWSC02TurnOffMorphScreen = 20;
+static const ExtraID kWSC02SouthViewNoArgon = 21;
+static const ExtraID kMessagesMovedToOffice = 22;
+static const ExtraID kMessagesOff = 23;
+static const ExtraID kMessagesZoomOutNoNitrogen = 24;
+static const ExtraID kMessagesMovedToOfficeNoNitrogen = 25;
+static const ExtraID kMessagesOffNoNitrogen = 26;
+static const ExtraID kMessagesViewNoNitrogen = 27;
+static const ExtraID kMessagesViewMachineOnNoNitrogen = 28;
+static const ExtraID kW03NorthActivate = 29;
+static const ExtraID kW03NorthGetData = 30;
+static const ExtraID kW03NorthInstructions = 31;
+static const ExtraID kW03NorthPrepMolecule1 = 32;
+static const ExtraID kW03NorthPrepMolecule2 = 33;
+static const ExtraID kW03NorthPrepMolecule3 = 34;
+static const ExtraID kW03NorthFinishSynthesis = 35;
+static const ExtraID kW03SouthCreateAntidote = 36;
+static const ExtraID kW03SouthAntidoteLoop = 37;
+static const ExtraID kW03SouthDeactivate = 38;
+static const ExtraID kW03SouthViewNoAntidote = 39;
+static const ExtraID kWSC07SouthMap = 40;
+static const ExtraID kW17WestPeopleCrossing = 41;
+static const ExtraID kW17WestPeopleCrossingView = 42;
+static const ExtraID kW21SouthPeopleCrossing = 43;
+static const ExtraID kW24SouthPeopleCrossing = 44;
+static const ExtraID kW34EastPeopleCrossing = 45;
+static const ExtraID kW36WestPeopleCrossing = 46;
+static const ExtraID kW38NorthPeopleCrossing = 47;
+static const ExtraID kW46SouthPeopleCrossing = 48;
+static const ExtraID kW49NorthPeopleCrossing = 49;
+static const ExtraID kW49NorthPeopleCrossingView = 50;
+static const ExtraID kWSC56SouthMap = 51;
+static const ExtraID kNerdAtTheDoor1 = 52;
+static const ExtraID kNerdAtTheDoor2 = 53;
+static const ExtraID kW61SouthZoomInNoGun = 54;
+static const ExtraID kW61Brochure = 55;
+static const ExtraID kW61SouthScreenOnWithGun = 56;
+static const ExtraID kW61SouthScreenOffWithGun = 57;
+static const ExtraID kW61SouthSmartAlloysWithGun = 58;
+static const ExtraID kW61SouthMorphingWithGun = 59;
+static const ExtraID kW61SouthTimeBendingWithGun = 60;
+static const ExtraID kW61SouthZoomOutNoGun = 61;
+static const ExtraID kW61SouthScreenOnNoGun = 62;
+static const ExtraID kW61SouthScreenOffNoGun = 63;
+static const ExtraID kW61SouthSmartAlloysNoGun = 64;
+static const ExtraID kW61SouthMorphingNoGun = 65;
+static const ExtraID kW61SouthTimeBendingNoGun = 66;
+static const ExtraID kW61MessagesOn = 67;
+static const ExtraID kW61MessagesOff = 68;
+static const ExtraID kW61WhiteMessage = 69;
+static const ExtraID kW61WalchekMessage = 70;
+static const ExtraID kW61WalchekEasterEgg1 = 71;
+static const ExtraID kW62SouthPlasmaRobotAppears = 72;
+static const ExtraID kW62ZoomToRobot = 73;
+static const ExtraID kW62ZoomOutFromRobot = 74;
+static const ExtraID kW62PlasmaDodgeSurvive = 75;
+static const ExtraID kW62PlasmaDodgeDie = 76;
+static const ExtraID kW65SouthSinclairLecture = 77;
+static const ExtraID kW73WestPeopleCrossing = 78;
+static const ExtraID kW73WestPeopleCrossingView = 79;
+static const ExtraID kW0ZSpottedByWomen = 80;
+static const ExtraID kW95RobotShoots = 81;
+static const ExtraID kW98MorphsToRobot = 82;
+static const ExtraID kW98RobotShoots = 83;
+static const ExtraID kW98RobotShocked = 84;
+static const ExtraID kW98RobotGassed = 85;
+static const ExtraID kW98RobotHeadOpensDark = 86;
+static const ExtraID kW98RobotHead000Dark = 87;
+static const ExtraID kW98RobotHead001Dark = 88;
+static const ExtraID kW98RobotHead010Dark = 89;
+static const ExtraID kW98RobotHead011Dark = 90;
+static const ExtraID kW98RobotHead100Dark = 91;
+static const ExtraID kW98RobotHead101Dark = 92;
+static const ExtraID kW98RobotHead110Dark = 93;
+static const ExtraID kW98RobotHead111Dark = 94;
+static const ExtraID kW98RobotHeadClosesDark = 95;
+static const ExtraID kW98WestViewWithGunDark = 96;
+static const ExtraID kW98WestViewNoGunDark = 97;
+static const ExtraID kW98RobotHeadOpensLight = 98;
+static const ExtraID kW98RobotHead000Light = 99;
+static const ExtraID kW98RobotHead001Light = 100;
+static const ExtraID kW98RobotHead010Light = 101;
+static const ExtraID kW98RobotHead011Light = 102;
+static const ExtraID kW98RobotHead100Light = 103;
+static const ExtraID kW98RobotHead101Light = 104;
+static const ExtraID kW98RobotHead110Light = 105;
+static const ExtraID kW98RobotHead111Light = 106;
+static const ExtraID kW98RobotHeadClosesLight = 107;
+static const ExtraID kW98WestViewWithGunLight = 108;
+static const ExtraID kW98WestViewNoGunLight = 109;
+
+static const CoordType kMoleculesMovieLeft = kNavAreaLeft + 112;
+static const CoordType kMoleculesMovieTop = kNavAreaTop + 40;
+
+WSC::WSC(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "WSC", kWSCID),
+ _moleculesMovie(kNoDisplayElement) {
+ setIsItemTaken(kArgonCanister);
+ setIsItemTaken(kSinclairKey);
+ setIsItemTaken(kNitrogenCanister);
+ setIsItemTaken(kPoisonDart);
+ setIsItemTaken(kAntidote);
+ setIsItemTaken(kMachineGun);
+ setIsItemTaken(kStunGun);
+
+ GameState.setTakenItemID(kArgonPickup, GameState.isTakenItemID(kArgonCanister) &&
+ GameState.isTakenItemID(kSinclairKey));
+}
+
+uint16 WSC::getDateResID() const {
+ return kDate2310ID;
+}
+
+void WSC::init() {
+ Neighborhood::init();
+
+ _cachedZoomSpot = 0;
+ _argonSprite = 0;
+
+ // HACK: Fix the drag item for picking up the Sinclair Key Card
+ HotspotInfoTable::Entry *entry = findHotspotEntry(kWSC02SouthTakeArgonSpotID);
+ entry->hotspotItem = kArgonPickup;
+}
+
+void WSC::flushGameState() {
+ g_energyMonitor->saveCurrentEnergyValue();
+}
+
+void WSC::start() {
+ if (g_energyMonitor) {
+ g_energyMonitor->stopEnergyDraining();
+ g_energyMonitor->restoreLastEnergyValue();
+ _vm->resetEnergyDeathReason();
+ g_energyMonitor->startEnergyDraining();
+ }
+
+ if (!GameState.getWSCDidPlasmaDodge())
+ forceStridingStop(kWSC58, kSouth, kAltWSCNormal);
+
+ Neighborhood::start();
+}
+
+class PryDoorMessage : public AIPlayMessageAction {
+public:
+ PryDoorMessage() : AIPlayMessageAction("Images/AI/WSC/XW59SD3", false) {}
+
+protected:
+ virtual void performAIAction(AIRule *);
+};
+
+void PryDoorMessage::performAIAction(AIRule *rule) {
+ if (((PegasusEngine *)g_engine)->playerHasItemID(kShieldBiochip)
+ && ((PegasusEngine *)g_engine)->getCurrentBiochip()->getObjectID() != kShieldBiochip)
+ AIPlayMessageAction::performAIAction(rule);
+}
+
+void WSC::setUpAIRules() {
+ Neighborhood::setUpAIRules();
+
+ if (g_AIArea) {
+ AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/WSC/XW1WB1", false);
+ AILastExtraCondition *extraCondition = new AILastExtraCondition(kWSCDartScan1);
+ AIRule *rule = new AIRule(extraCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false);
+ AILocationCondition *locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC06, kNorth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC10, kWest));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC28, kWest));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC49, kWest));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC65, kSouth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC73, kSouth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC79, kWest));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/WSC/XW59SD1", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC58, kSouth));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ PryDoorMessage *pryDoorMessage = new PryDoorMessage();
+ AIDoorOpenedCondition *doorCondition = new AIDoorOpenedCondition(MakeRoomView(kWSC58, kSouth));
+ rule = new AIRule(doorCondition, pryDoorMessage);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/WSC/XW61E", false);
+ AIHasItemCondition *itemCondition = new AIHasItemCondition(kMachineGun);
+ rule = new AIRule(itemCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+
+ messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB1E", false);
+ locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kWSC95, kWest));
+ rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+ }
+}
+
+Common::String WSC::getBriefingMovie() {
+ return "Images/AI/WSC/XWO";
+}
+
+Common::String WSC::getEnvScanMovie() {
+ RoomID room = GameState.getCurrentRoom();
+
+ if (room >= kWSC01 && room <= kWSC04)
+ return "Images/AI/WSC/XWE1";
+ else if (room >= kWSC06 && room <= kWSC58)
+ return "Images/AI/WSC/XWE2";
+ else if (room >= kWSC60 && room <= kWSC61West)
+ return "Images/AI/WSC/XWE3";
+ else if (room >= kWSC64 && room <= kWSC98)
+ return "Images/AI/WSC/XWE4";
+
+ return "Images/AI/WSC/XWE5";
+}
+
+uint WSC::getNumHints() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC10, kWest):
+ case MakeRoomView(kWSC28, kWest):
+ case MakeRoomView(kWSC49, kWest):
+ case MakeRoomView(kWSC65, kSouth):
+ case MakeRoomView(kWSC75, kSouth):
+ case MakeRoomView(kWSC79, kWest):
+ return 2;
+ case MakeRoomView(kWSC02, kSouth):
+ if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison &&
+ !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) &&
+ !GameState.getWSCDesignedAntidote())
+ return 3;
+ else if (!GameState.getScoringGotNitrogenCanister() ||
+ !GameState.getScoringGotSinclairKey())
+ return 1;
+ break;
+ case MakeRoomView(kWSC03, kNorth):
+ if (inSynthesizerGame() || (_vm->getEnergyDeathReason() == kDeathDidntStopPoison &&
+ !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) &&
+ !GameState.getWSCDesignedAntidote()))
+ return 3;
+ break;
+ case MakeRoomView(kWSC01, kNorth):
+ case MakeRoomView(kWSC01, kSouth):
+ case MakeRoomView(kWSC01, kEast):
+ case MakeRoomView(kWSC01, kWest):
+ case MakeRoomView(kWSC02, kNorth):
+ case MakeRoomView(kWSC02, kEast):
+ case MakeRoomView(kWSC02, kWest):
+ case MakeRoomView(kWSC02Morph, kNorth):
+ case MakeRoomView(kWSC02Morph, kEast):
+ case MakeRoomView(kWSC02Morph, kWest):
+ case MakeRoomView(kWSC02Messages, kNorth):
+ case MakeRoomView(kWSC02Messages, kEast):
+ case MakeRoomView(kWSC02Messages, kWest):
+ case MakeRoomView(kWSC03, kSouth):
+ case MakeRoomView(kWSC03, kEast):
+ case MakeRoomView(kWSC03, kWest):
+ case MakeRoomView(kWSC04, kNorth):
+ case MakeRoomView(kWSC04, kSouth):
+ case MakeRoomView(kWSC04, kEast):
+ case MakeRoomView(kWSC04, kWest):
+ if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison &&
+ !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) &&
+ !GameState.getWSCDesignedAntidote())
+ return 3;
+ break;
+ case MakeRoomView(kWSC02Messages, kSouth):
+ if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison &&
+ !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) &&
+ !GameState.getWSCDesignedAntidote())
+ return 3;
+ else if (!GameState.getScoringGotNitrogenCanister())
+ return 1;
+ break;
+ case MakeRoomView(kWSC02Morph, kSouth):
+ if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison &&
+ !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) &&
+ !GameState.getWSCDesignedAntidote())
+ return 3;
+ else if (!GameState.getScoringGotSinclairKey())
+ return 1;
+ break;
+ case MakeRoomView(kWSC42, kEast):
+ if (!GameState.isCurrentDoorOpen())
+ return 1;
+ break;
+ case MakeRoomView(kWSC58, kSouth):
+ if (GameState.isCurrentDoorOpen()) {
+ if (GameState.getWSCDidPlasmaDodge())
+ return 0;
+ else
+ return 1;
+ } else if (_vm->playerHasItemID(kCrowbar))
+ return 2;
+
+ return 3;
+ case MakeRoomView(kWSC61, kEast):
+ if (!GameState.getScoringSawBrochure())
+ return 1;
+ break;
+ case MakeRoomView(kWSC61, kSouth):
+ if (!GameState.getScoringSawSinclairEntry1() ||
+ !GameState.getScoringSawSinclairEntry2() ||
+ !GameState.getScoringSawSinclairEntry3())
+ return 1;
+ break;
+ case MakeRoomView(kWSC98, kWest):
+ if (getCurrentActivation() == kActivationRobotTurning)
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+Common::String WSC::getHintMovie(uint hintNum) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC10, kWest):
+ case MakeRoomView(kWSC28, kWest):
+ case MakeRoomView(kWSC49, kWest):
+ case MakeRoomView(kWSC65, kSouth):
+ case MakeRoomView(kWSC75, kSouth):
+ case MakeRoomView(kWSC79, kWest):
+ if (hintNum == 1)
+ return "Images/AI/Globals/XGLOB5B";
+
+ return "Images/AI/Globals/XGLOB5C";
+ case MakeRoomView(kWSC02, kSouth):
+ if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison &&
+ !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) &&
+ !GameState.getWSCDesignedAntidote())
+ return Common::String::format("Images/AI/WSC/XWPH%d", hintNum);
+
+ return "Images/AI/Globals/XGLOB1C";
+ case MakeRoomView(kWSC61, kEast):
+ case MakeRoomView(kWSC61, kSouth):
+ return "Images/AI/Globals/XGLOB1C";
+ case MakeRoomView(kWSC42, kEast):
+ if (_vm->playerHasItemID(kSinclairKey))
+ return "Images/AI/Globals/XGLOB1A";
+
+ return "Images/AI/Globals/XGLOB2C";
+ case MakeRoomView(kWSC58, kSouth):
+ switch (hintNum) {
+ case 1:
+ if (GameState.isCurrentDoorOpen()) {
+ // Only get here if we haven't done the plasma dodge game...
+ if (_vm->playerHasItemID(kShieldBiochip))
+ return "Images/AI/Globals/XGLOB1A";
+ else
+ return "Images/AI/Globals/XGLOB3F";
+ } else if (_vm->playerHasItemID(kCrowbar)) {
+ return "Images/AI/Globals/XGLOB1A";
+ }
+
+ return "Images/AI/Globals/XGLOB1B";
+ case 2:
+ // Only get here if the door is still locked...
+ if (_vm->playerHasItemID(kCrowbar))
+ return "Images/AI/WSC/XW59SD2";
+
+ return "Images/AI/Globals/XGLOB2D";
+ case 3:
+ // Only get here if the door is still locked and we don't have the
+ // crowbar...
+ return "Images/AI/WSC/XW59SD2";
+ }
+ break;
+ case MakeRoomView(kWSC03, kNorth):
+ if (inSynthesizerGame())
+ return Common::String::format("Images/AI/WSC/XW03NH%d", hintNum);
+
+ return Common::String::format("Images/AI/WSC/XWPH%d", hintNum);
+ case MakeRoomView(kWSC01, kNorth):
+ case MakeRoomView(kWSC01, kSouth):
+ case MakeRoomView(kWSC01, kEast):
+ case MakeRoomView(kWSC01, kWest):
+ case MakeRoomView(kWSC02, kNorth):
+ case MakeRoomView(kWSC02, kEast):
+ case MakeRoomView(kWSC02, kWest):
+ case MakeRoomView(kWSC02Morph, kNorth):
+ case MakeRoomView(kWSC02Morph, kEast):
+ case MakeRoomView(kWSC02Morph, kWest):
+ case MakeRoomView(kWSC02Messages, kNorth):
+ case MakeRoomView(kWSC02Messages, kEast):
+ case MakeRoomView(kWSC02Messages, kWest):
+ case MakeRoomView(kWSC03, kSouth):
+ case MakeRoomView(kWSC03, kEast):
+ case MakeRoomView(kWSC03, kWest):
+ case MakeRoomView(kWSC04, kNorth):
+ case MakeRoomView(kWSC04, kSouth):
+ case MakeRoomView(kWSC04, kEast):
+ case MakeRoomView(kWSC04, kWest):
+ // analyzer hint
+ return Common::String::format("Images/AI/WSC/XWPH%d", hintNum);
+ case MakeRoomView(kWSC02Messages, kSouth):
+ case MakeRoomView(kWSC02Morph, kSouth):
+ if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison &&
+ !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) &&
+ !GameState.getWSCDesignedAntidote())
+ // analyzer hint
+ return Common::String::format("Images/AI/WSC/XWPH%d", hintNum);
+
+ return "Images/AI/Globals/XGLOB1C";
+ case MakeRoomView(kWSC98, kWest):
+ return "Images/AI/WSC/XW98WH2";
+ }
+
+ return "";
+}
+
+void WSC::prepareForAIHint(const Common::String &movieName) {
+ if (movieName == "Images/AI/WSC/XW98WH2" && isEventTimerRunning())
+ pauseTimer();
+}
+
+void WSC::cleanUpAfterAIHint(const Common::String &movieName) {
+ if (movieName == "Images/AI/WSC/XW98WH2" && isEventTimerRunning())
+ resumeTimer();
+}
+
+bool WSC::okayToJump() {
+ if (GameState.getWSCPoisoned()) {
+ die(kDeathDidntStopPoison);
+ return false;
+ }
+
+ bool result = Neighborhood::okayToJump();
+ if (!result)
+ playSpotSoundSync(kWSCCantTransportIn, kWSCCantTransportOut);
+
+ return result;
+}
+
+TimeValue WSC::getViewTime(const RoomID room, const DirectionConstant direction) {
+ ExtraID viewExtra = 0xffffffff;
+ ExtraTable::Entry extra;
+
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kWSC01, kWest):
+ if (!GameState.getWSCSeenTimeStream()) {
+ getExtraEntry(kWSCArrivalFromTSA, extra);
+ return extra.movieStart;
+ } else if (GameState.getWSCPoisoned() && !GameState.getWSCAnsweredAboutDart()) {
+ viewExtra = kWSCDartScan1;
+ }
+ break;
+ case MakeRoomView(kWSC02Morph, kSouth):
+ if (GameState.isTakenItemID(kArgonPickup) || GameState.isTakenItemID(kArgonCanister))
+ viewExtra = kWSC02SouthViewNoArgon;
+ break;
+ case MakeRoomView(kWSC02Messages, kSouth):
+ if (GameState.isTakenItemID(kNitrogenCanister)) {
+ if (_privateFlags.getFlag(kWSCPrivateLabMessagesOpenFlag))
+ viewExtra = kMessagesViewMachineOnNoNitrogen;
+ else
+ viewExtra = kMessagesViewNoNitrogen;
+ }
+ break;
+ case MakeRoomView(kWSC03, kSouth):
+ if (_privateFlags.getFlag(kWSCDraggingAntidoteFlag))
+ viewExtra = kW03SouthViewNoAntidote;
+ break;
+ case MakeRoomView(kWSC17, kWest):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt17WestFlag))
+ viewExtra = kW17WestPeopleCrossingView;
+ break;
+ case MakeRoomView(kWSC49, kNorth):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt49NorthFlag))
+ viewExtra = kW49NorthPeopleCrossingView;
+ break;
+ case MakeRoomView(kWSC73, kWest):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt73WestFlag))
+ viewExtra = kW73WestPeopleCrossingView;
+ break;
+ case MakeRoomView(kWSC98, kWest):
+ if (GameState.getWSCRobotDead()) {
+ if (GameState.getWSCRobotGone()) {
+ if (GameState.isTakenItemID(kStunGun)) {
+ if (GameState.getWSCCatwalkDark())
+ viewExtra = kW98WestViewNoGunDark;
+ else
+ viewExtra = kW98WestViewNoGunLight;
+ } else {
+ if (GameState.getWSCCatwalkDark())
+ viewExtra = kW98WestViewWithGunDark;
+ else
+ viewExtra = kW98WestViewWithGunLight;
+ }
+ } else if (_privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) {
+ if (GameState.getWSCCatwalkDark())
+ viewExtra = kW98RobotHead111Dark;
+ else
+ viewExtra = kW98RobotHead111Light;
+
+ if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag))
+ viewExtra -= 1;
+ if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag))
+ viewExtra -= 2;
+ if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag))
+ viewExtra -= 4;
+ } else if (GameState.getWSCRobotDead()) {
+ // Should only happen on loading a saved game, so it can take its time.
+ if (GameState.getWSCCatwalkDark())
+ viewExtra = kW98RobotShocked;
+ else
+ viewExtra = kW98RobotGassed;
+ }
+ }
+ break;
+ }
+
+ if (viewExtra != 0xffffffff) {
+ getExtraEntry(viewExtra, extra);
+ return extra.movieEnd - 1;
+ }
+
+ return Neighborhood::getViewTime(room, direction);
+}
+
+void WSC::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kWSC58, kSouth):
+ case MakeRoomView(kWSC79, kWest):
+ if ((flags & kSpotOnTurnMask) != 0) {
+ spotEntry.clear();
+ return;
+ }
+ break;
+ }
+
+ Neighborhood::findSpotEntry(room, direction, flags, spotEntry);
+}
+
+void WSC::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) {
+ Neighborhood::getZoomEntry(id, zoomEntry);
+
+ ExtraTable::Entry extra;
+ ExtraID zoomExtra = 0xffffffff;
+
+ switch (id) {
+ case kWSC02SouthMessagesSpotID:
+ if (GameState.isTakenItemID(kNitrogenCanister))
+ zoomExtra = kWSC02MessagesZoomNoNitrogen;
+ break;
+ case kWSC02SouthMessagesOutSpotID:
+ if (GameState.isTakenItemID(kNitrogenCanister))
+ zoomExtra = kMessagesZoomOutNoNitrogen;
+ break;
+ case kWSC02SouthMorphSpotID:
+ if (GameState.isTakenItemID(kArgonCanister))
+ zoomExtra = kWSC02MorphZoomNoArgon;
+ break;
+ case kWSC02SouthMorphOutSpotID:
+ if (GameState.isTakenItemID(kArgonCanister))
+ zoomExtra = kWSC02ZoomOutNoArgon;
+ break;
+ case kWSC61SouthSpotID:
+ if (GameState.isTakenItemID(kMachineGun))
+ zoomExtra = kW61SouthZoomInNoGun;
+ break;
+ case kWSC61SouthOutSpotID:
+ if (GameState.isTakenItemID(kMachineGun))
+ zoomExtra = kW61SouthZoomOutNoGun;
+ break;
+ }
+
+ if (zoomExtra != 0xffffffff) {
+ getExtraEntry(zoomExtra, extra);
+ zoomEntry.movieStart = extra.movieStart;
+ zoomEntry.movieEnd = extra.movieEnd;
+ }
+}
+
+void WSC::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) {
+ switch (id) {
+ case kWSCZoomOutFromAnalyzer:
+ Neighborhood::getExtraEntry(kWSCZoomOutFromAnalyzer, extraEntry);
+ extraEntry.movieEnd = extraEntry.movieStart + 14 * kWSCFrameDuration;
+ break;
+ case kW61WalchekMessage:
+ if (GameState.getEasterEgg())
+ Neighborhood::getExtraEntry(kW61WalchekEasterEgg1, extraEntry);
+ else
+ Neighborhood::getExtraEntry(id, extraEntry);
+ break;
+ case kW61SouthScreenOnWithGun:
+ if (GameState.isTakenItemID(kMachineGun))
+ Neighborhood::getExtraEntry(id, extraEntry);
+ else
+ Neighborhood::getExtraEntry(kW61SouthScreenOnNoGun, extraEntry);
+ break;
+ case kW61SouthSmartAlloysWithGun:
+ if (GameState.isTakenItemID(kMachineGun))
+ Neighborhood::getExtraEntry(id, extraEntry);
+ else
+ Neighborhood::getExtraEntry(kW61SouthSmartAlloysNoGun, extraEntry);
+ break;
+ case kW61SouthMorphingWithGun:
+ if (GameState.isTakenItemID(kMachineGun))
+ Neighborhood::getExtraEntry(id, extraEntry);
+ else
+ Neighborhood::getExtraEntry(kW61SouthMorphingNoGun, extraEntry);
+ break;
+ case kW61SouthTimeBendingWithGun:
+ if (GameState.isTakenItemID(kMachineGun))
+ Neighborhood::getExtraEntry(id, extraEntry);
+ else
+ Neighborhood::getExtraEntry(kW61SouthTimeBendingNoGun, extraEntry);
+ break;
+ case kW98RobotHeadOpensLight:
+ if (GameState.getWSCCatwalkDark())
+ Neighborhood::getExtraEntry(kW98RobotHeadOpensDark, extraEntry);
+ else
+ Neighborhood::getExtraEntry(id, extraEntry);
+ break;
+ default:
+ Neighborhood::getExtraEntry(id, extraEntry);
+ break;
+ }
+}
+
+CanMoveForwardReason WSC::canMoveForward(ExitTable::Entry &entry) {
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC01, kWest) &&
+ getCurrentActivation() != kActivateHotSpotAlways)
+ return kCantMoveWatchingDiagnosis;
+
+ return Neighborhood::canMoveForward(entry);
+}
+
+// Also add cases here for compound analyzer...
+CanTurnReason WSC::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC01, kWest):
+ if (getCurrentActivation() != kActivateHotSpotAlways)
+ return kCantTurnWatchingDiagnosis;
+ break;
+ case MakeRoomView(kWSC01, kEast):
+ if (getCurrentActivation() != kActivateHotSpotAlways)
+ return kCantTurnWatchingAnalysis;
+ break;
+ case MakeRoomView(kWSC03, kNorth):
+ if (_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag))
+ return kCantTurnInMoleculeGame;
+ break;
+ }
+
+ return Neighborhood::canTurn(turnDirection, nextDir);
+}
+
+CanOpenDoorReason WSC::canOpenDoor(DoorTable::Entry &entry) {
+ switch (GameState.getCurrentRoom()) {
+ case kWSC42:
+ if (!_privateFlags.getFlag(kWSCPrivateSinclairOfficeOpenFlag))
+ return kCantOpenLocked;
+ break;
+ case kWSC58:
+ if (!_privateFlags.getFlag(kWSCPrivate58SouthOpenFlag))
+ return kCantOpenLocked;
+ break;
+ }
+
+ return Neighborhood::canOpenDoor(entry);
+}
+
+void WSC::bumpIntoWall() {
+ requestSpotSound(kWSCBumpIntoWallIn, kWSCBumpIntoWallOut, kFilterAllInput, 0);
+ Neighborhood::bumpIntoWall();
+}
+
+void WSC::closeDoorOffScreen(const RoomID room, const DirectionConstant) {
+ Item *keyCard;
+
+ switch (room) {
+ case kWSC58:
+ case kWSC62:
+ case kWSC63:
+ case kWSC64:
+ case kWSC85:
+ case kWSC86:
+ case kWSC88:
+ case kWSC89:
+ playSpotSoundSync(kSlidingDoorCloseIn, kSlidingDoorCloseOut);
+ break;
+ case kWSC81:
+ case kWSC82:
+ case kWSC92:
+ case kWSC93:
+ keyCard = _vm->getAllItems().findItemByID(kKeyCard);
+ if (keyCard->getItemState() == kFlashlightOn && (GameState.getCurrentRoom() == kWSC81 ||
+ GameState.getCurrentRoom() == kWSC93)) {
+ keyCard->setItemState(kFlashlightOff);
+ playSpotSoundSync(kWSCFlashlightClickIn, kWSCFlashlightClickOut);
+ } else if (keyCard->getItemState() == kFlashlightOff && (GameState.getCurrentRoom() == kWSC82 ||
+ GameState.getCurrentRoom() == kWSC92)) {
+ keyCard->setItemState(kFlashlightOn);
+ playSpotSoundSync(kWSCFlashlightClickIn, kWSCFlashlightClickOut);
+ }
+
+ playSpotSoundSync(kSlimyDoorCloseIn, kSlimyDoorCloseOut);
+ break;
+ default:
+ playSpotSoundSync(kSlimyDoorCloseIn, kSlimyDoorCloseOut);
+ break;
+ }
+}
+
+void WSC::cantMoveThatWay(CanMoveForwardReason reason) {
+ if (reason != kCantMoveWatchingDiagnosis)
+ Neighborhood::cantMoveThatWay(reason);
+}
+
+void WSC::cantOpenDoor(CanOpenDoorReason reason) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC22, kWest):
+ playSpotSoundSync(kNakamuraNotHomeIn, kNakamuraNotHomeOut);
+ break;
+ case MakeRoomView(kWSC23, kEast):
+ playSpotSoundSync(kHernandezNotHomeIn, kHernandezNotHomeOut);
+ break;
+ case MakeRoomView(kWSC26, kWest):
+ playSpotSoundSync(kGrailisNotHomeIn, kGrailisNotHomeOut);
+ break;
+ case MakeRoomView(kWSC27, kEast):
+ playSpotSoundSync(kWashingtonNotHomeIn, kWashingtonNotHomeOut);
+ break;
+ case MakeRoomView(kWSC32, kWest):
+ playSpotSoundSync(kTheriaultNotHomeIn, kTheriaultNotHomeOut);
+ break;
+ case MakeRoomView(kWSC33, kEast):
+ playSpotSoundSync(kSullivanNotHomeIn, kSullivanNotHomeOut);
+ break;
+ case MakeRoomView(kWSC41, kWest):
+ playSpotSoundSync(kGlennerNotHomeIn, kGlennerNotHomeOut);
+ break;
+ case MakeRoomView(kWSC42, kEast):
+ playSpotSoundSync(kSinclairNotHomeIn, kSinclairNotHomeOut);
+ break;
+ case MakeRoomView(kWSC15, kWest):
+ case MakeRoomView(kWSC25, kWest):
+ case MakeRoomView(kWSC33, kWest):
+ case MakeRoomView(kWSC41, kEast):
+ case MakeRoomView(kWSC46, kWest):
+ playSpotSoundSync(kWSCLabClosedIn, kWSCLabClosedOut);
+ break;
+ default:
+ Neighborhood::cantOpenDoor(reason);
+ break;
+ }
+}
+
+void WSC::doorOpened() {
+ Neighborhood::doorOpened();
+
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC42, kEast):
+ _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kSinclairKey));
+ break;
+ case MakeRoomView(kWSC58, kSouth):
+ GameState.setScoringUsedCrowBarInWSC();
+ _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kCrowbar));
+ break;
+ case MakeRoomView(kWSC06, kNorth):
+ case MakeRoomView(kWSC79, kWest):
+ die(kDeathArrestedInWSC);
+ break;
+ case MakeRoomView(kWSC60, kWest):
+ if (_vm->itemInInventory(kMachineGun))
+ startExtraSequence(kNerdAtTheDoor2, kExtraCompletedFlag, kFilterNoInput);
+ else if (!GameState.getWSCSeenNerd())
+ startExtraSequence(kNerdAtTheDoor1, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC95, kWest):
+ GameState.setScoringOpenedCatwalk();
+ scheduleEvent(kGawkAtRobotTime, 1, kTimerEventPlayerGawkingAtRobot);
+ break;
+ }
+}
+
+void WSC::turnLeft() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC17, kNorth):
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt17WestFlag) && _vm->getRandomNumber(2) == 0)
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, true);
+ break;
+ case MakeRoomView(kWSC49, kEast):
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt49NorthFlag) && _vm->getRandomNumber(2) == 0)
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, true);
+ break;
+ case MakeRoomView(kWSC73, kNorth):
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt73WestFlag) && _vm->getRandomNumber(2) == 0)
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, true);
+ break;
+ case MakeRoomView(kWSC73, kWest):
+ if (!GameState.getWSCBeenAtWSC93())
+ setCurrentAlternate(kAltWSCW0ZDoorOpen);
+ break;
+ case MakeRoomView(kWSC95, kWest):
+ cancelEvent();
+ break;
+ }
+
+ Neighborhood::turnLeft();
+}
+
+void WSC::turnRight() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC17, kSouth):
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt17WestFlag) && _vm->getRandomNumber(2) == 0)
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, true);
+ break;
+ case MakeRoomView(kWSC49, kWest):
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt49NorthFlag) && _vm->getRandomNumber(2) == 0)
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, true);
+ break;
+ case MakeRoomView(kWSC73, kSouth):
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt73WestFlag) && _vm->getRandomNumber(2) == 0)
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, true);
+ break;
+ case MakeRoomView(kWSC73, kEast):
+ if (!GameState.getWSCBeenAtWSC93())
+ setCurrentAlternate(kAltWSCW0ZDoorOpen);
+ break;
+ case MakeRoomView(kWSC95, kWest):
+ cancelEvent();
+ break;
+ }
+
+ Neighborhood::turnRight();
+}
+
+void WSC::moveForward() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC19, kNorth):
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt19NorthFlag))
+ setCurrentAlternate(kAltWSCPeopleAtW19North);
+ break;
+ case MakeRoomView(kWSC95, kWest):
+ cancelEvent();
+ break;
+ }
+
+ Neighborhood::moveForward();
+}
+
+void WSC::zoomTo(const Hotspot *hotspot) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC02Messages, kSouth):
+ if (_privateFlags.getFlag(kWSCPrivateLabMessagesOpenFlag)) {
+ _cachedZoomSpot = hotspot;
+ if (GameState.isTakenItemID(kNitrogenCanister))
+ startExtraSequence(kMessagesOffNoNitrogen, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMessagesOff, kExtraCompletedFlag, kFilterNoInput);
+ return;
+ }
+ break;
+ case MakeRoomView(kWSC61West, kWest):
+ if (GameState.getWSCOfficeMessagesOpen()) {
+ _cachedZoomSpot = hotspot;
+ startExtraSequence(kW61MessagesOff, kExtraCompletedFlag, kFilterNoInput);
+ return;
+ }
+ break;
+ case MakeRoomView(kWSC61South, kSouth):
+ if (_privateFlags.getFlag(kWSCPrivateOfficeLogOpenFlag)) {
+ _cachedZoomSpot = hotspot;
+ if (GameState.isTakenItemID(kMachineGun))
+ startExtraSequence(kW61SouthScreenOffNoGun, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kW61SouthScreenOffWithGun, kExtraCompletedFlag, kFilterNoInput);
+ return;
+ }
+ break;
+ }
+
+ Neighborhood::zoomTo(hotspot);
+}
+
+void WSC::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
+ if (extraID == kW61Brochure)
+ loadLoopSound1("");
+
+ Neighborhood::startExtraSequence(extraID, flags, interruptionFilter);
+}
+
+int16 WSC::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
+ int16 angle = Neighborhood::getStaticCompassAngle(room, dir);
+
+ switch (room) {
+ case kWSC02Messages:
+ angle -= 50;
+ break;
+ case kWSC02Morph:
+ angle += 5;
+ break;
+ case kWSC60East:
+ angle -= 10;
+ break;
+ case kWSC66:
+ angle -= kAuditoriumAngleOffset;
+ break;
+ case kWSC67:
+ angle += kAuditoriumAngleOffset;
+ break;
+ case kWSC68:
+ angle -= kAuditoriumAngleOffset * 2;
+ break;
+ case kWSC69:
+ angle += kAuditoriumAngleOffset * 2;
+ break;
+ case kWSC70:
+ angle -= kAuditoriumAngleOffset * 3;
+ break;
+ case kWSC71:
+ angle += kAuditoriumAngleOffset * 3;
+ break;
+ case kWSC72:
+ if (dir == kEast || dir == kWest)
+ angle -= kAuditoriumAngleOffset * 4;
+ break;
+ case kWSC73:
+ if (dir == kEast || dir == kWest)
+ angle += kAuditoriumAngleOffset * 4;
+ break;
+ }
+
+ return angle;
+}
+
+void WSC::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) {
+ Neighborhood::getExitCompassMove(exitEntry, compassMove);
+
+ if (exitEntry.room == kWSC65 && exitEntry.direction == kSouth) {
+ compassMove.insertFaderKnot(exitEntry.movieStart + 100 * kWSCFrameDuration, 180);
+ compassMove.insertFaderKnot(exitEntry.movieStart + 108 * kWSCFrameDuration, 150);
+ compassMove.insertFaderKnot(exitEntry.movieEnd, 150);
+ }
+}
+
+void WSC::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) {
+ switch (entry.extra) {
+ case kW61Brochure:
+ compassMove.insertFaderKnot(entry.movieStart + 15 * kWSCFrameDuration, 85);
+ compassMove.insertFaderKnot(entry.movieEnd - 15 * kWSCFrameDuration, 85);
+ compassMove.insertFaderKnot(entry.movieEnd, 90);
+ break;
+ default:
+ Neighborhood::getExtraCompassMove(entry, compassMove);
+ break;
+ }
+}
+
+void WSC::loadAmbientLoops() {
+ RoomID room = GameState.getCurrentRoom();
+
+ if (room >= kWSC01 && room <= kWSC04) {
+ if (GameState.getWSCSeenTimeStream())
+ loadLoopSound1("Sounds/World Science Center/WLabLoop.22K.AIFF", 0x100 / 2);
+ } else if ((room >= kWSC06 && room <= kWSC58) || (room >= kWSC62 && room <= kWSC63))
+ loadLoopSound1("Sounds/World Science Center/Organic Walls.22K.AIFF", 0x100 / 2);
+ else if (room >= kWSC82 && room <= kWSC92)
+ loadLoopSound1("Sounds/World Science Center/Creature Feature.22K.AIFF");
+ else if ((room >= kWSC60 && room <= kWSC61West) || (room >= kWSC64 && room <= kWSC81) ||
+ (room >= kWSC93 && room <= kWSC97))
+ loadLoopSound1("Sounds/World Science Center/The Other Side.22K.AIFF", 0x100 / 12);
+ else if (room == kWSC98)
+ loadLoopSound1("Sounds/World Science Center/WCatLoop.22K.AIFF");
+}
+
+void WSC::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kWSC07, kNorth):
+ case MakeRoomView(kWSC11, kSouth):
+ case MakeRoomView(kWSC13, kSouth):
+ case MakeRoomView(kWSC13, kWest):
+ case MakeRoomView(kWSC16, kWest):
+ case MakeRoomView(kWSC17, kEast):
+ case MakeRoomView(kWSC19, kWest):
+ case MakeRoomView(kWSC28, kNorth):
+ case MakeRoomView(kWSC28, kSouth):
+ case MakeRoomView(kWSC28, kEast):
+ case MakeRoomView(kWSC28, kWest):
+ case MakeRoomView(kWSC29, kNorth):
+ case MakeRoomView(kWSC29, kSouth):
+ case MakeRoomView(kWSC29, kEast):
+ case MakeRoomView(kWSC29, kWest):
+ case MakeRoomView(kWSC40, kEast):
+ case MakeRoomView(kWSC42, kEast):
+ case MakeRoomView(kWSC49, kWest):
+ case MakeRoomView(kWSC49, kNorth):
+ case MakeRoomView(kWSC50, kNorth):
+ case MakeRoomView(kWSC55, kEast):
+ case MakeRoomView(kWSC65, kSouth):
+ case MakeRoomView(kWSC65, kEast):
+ case MakeRoomView(kWSC65, kWest):
+ case MakeRoomView(kWSC72, kEast):
+ case MakeRoomView(kWSC72, kSouth):
+ case MakeRoomView(kWSC73, kWest):
+ case MakeRoomView(kWSC73, kSouth):
+ case MakeRoomView(kWSC79, kWest):
+ case MakeRoomView(kWSC81, kEast):
+ case MakeRoomView(kWSC93, kNorth):
+ case MakeRoomView(kWSC95, kWest):
+ makeContinuePoint();
+ break;
+ case MakeRoomView(kWSC58, kSouth):
+ if (!GameState.getWSCDidPlasmaDodge())
+ makeContinuePoint();
+ break;
+ case MakeRoomView(kWSC60, kWest):
+ if (_vm->playerHasItemID(kMachineGun))
+ makeContinuePoint();
+ break;
+ }
+}
+
+void WSC::arriveAt(const RoomID room, const DirectionConstant dir) {
+ switch (MakeRoomView(room, dir)) {
+ case MakeRoomView(kWSC60, kNorth):
+ case MakeRoomView(kWSC60, kSouth):
+ case MakeRoomView(kWSC60, kEast):
+ case MakeRoomView(kWSC60, kWest):
+ case MakeRoomView(kWSC60East, kNorth):
+ case MakeRoomView(kWSC60East, kSouth):
+ case MakeRoomView(kWSC60East, kEast):
+ case MakeRoomView(kWSC60East, kWest):
+ case MakeRoomView(kWSC60North, kNorth):
+ case MakeRoomView(kWSC60North, kSouth):
+ case MakeRoomView(kWSC60North, kEast):
+ case MakeRoomView(kWSC60North, kWest):
+ case MakeRoomView(kWSC61, kNorth):
+ case MakeRoomView(kWSC61, kSouth):
+ case MakeRoomView(kWSC61, kEast):
+ case MakeRoomView(kWSC61, kWest):
+ case MakeRoomView(kWSC61South, kNorth):
+ case MakeRoomView(kWSC61South, kSouth):
+ case MakeRoomView(kWSC61South, kEast):
+ case MakeRoomView(kWSC61South, kWest):
+ case MakeRoomView(kWSC61West, kNorth):
+ case MakeRoomView(kWSC61West, kSouth):
+ case MakeRoomView(kWSC61West, kEast):
+ case MakeRoomView(kWSC61West, kWest):
+ if (GameState.isTakenItemID(kMachineGun))
+ setCurrentAlternate(kAltWSCTookMachineGun);
+ else
+ setCurrentAlternate(kAltWSCNormal);
+ break;
+ case MakeRoomView(kWSC73, kSouth):
+ case MakeRoomView(kWSC75, kNorth):
+ case MakeRoomView(kWSC75, kSouth):
+ case MakeRoomView(kWSC75, kEast):
+ case MakeRoomView(kWSC75, kWest):
+ if (!GameState.getWSCBeenAtWSC93())
+ setCurrentAlternate(kAltWSCW0ZDoorOpen);
+ break;
+ }
+
+ Neighborhood::arriveAt(room, dir);
+
+ switch (MakeRoomView(room, dir)) {
+ case MakeRoomView(kWSC01, kWest):
+ if (!GameState.getWSCSeenTimeStream()) {
+ requestExtraSequence(kWSCArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kWSCShotByRobot, 0, kFilterNoInput);
+ requestExtraSequence(kWSCDartScan1, kExtraCompletedFlag, kFilterNoInput);
+ } else if (GameState.getWSCPoisoned() && !GameState.getWSCAnsweredAboutDart()) {
+ setCurrentActivation(kActivationShotByRobot);
+ }
+ break;
+ case MakeRoomView(kWSC01, kEast):
+ if (GameState.getWSCDartInAnalyzer())
+ requestExtraSequence(kWSCDropDartIntoAnalyzer, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC02Morph, kSouth):
+ setCurrentActivation(kActivationMorphScreenOff);
+ break;
+ case MakeRoomView(kWSC03, kNorth):
+ setCurrentActivation(kActivationW03NorthOff);
+ break;
+ case MakeRoomView(kWSC03, kSouth):
+ if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote())
+ setCurrentActivation(kActivationReadyForSynthesis);
+ break;
+ case MakeRoomView(kWSC16, kNorth):
+ if (getCurrentAlternate() == kAltWSCPeopleAtW19North) {
+ setCurrentAlternate(kAltWSCNormal);
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt19NorthFlag, true);
+ }
+ break;
+ case MakeRoomView(kWSC07, kSouth):
+ case MakeRoomView(kWSC56, kNorth):
+ setCurrentActivation(kActivationReadyForMap);
+ break;
+ case MakeRoomView(kWSC42, kWest):
+ setCurrentAlternate(kAltWSCNormal);
+ break;
+ case MakeRoomView(kWSC42, kEast):
+ _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false);
+ setCurrentActivation(kActivationSinclairOfficeLocked);
+ break;
+ case MakeRoomView(kWSC58, kSouth):
+ setCurrentActivation(kActivationW58SouthDoorLocked);
+ _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false);
+ break;
+ case MakeRoomView(kWSC60, kEast):
+ GameState.setScoringEnteredSinclairOffice();
+ break;
+ case MakeRoomView(kWSC61West, kWest):
+ setCurrentActivation(kActivationW61MessagesOff);
+ break;
+ case MakeRoomView(kWSC61South, kSouth):
+ setCurrentActivation(kActivationW61SouthOff);
+ break;
+ case MakeRoomView(kWSC62, kSouth):
+ if (!GameState.getWSCDidPlasmaDodge()) {
+ g_AIArea->lockAIOut();
+ loadLoopSound1("Sounds/World Science Center/Plasma Rock.22K.AIFF");
+ requestExtraSequence(kW62SouthPlasmaRobotAppears, 0, kFilterNoInput);
+ requestExtraSequence(kW62ZoomToRobot, 0, kFilterNoInput);
+ requestExtraSequence(kW62ZoomOutFromRobot, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case MakeRoomView(kWSC65Screen, kSouth):
+ if (!GameState.getWSCSeenSinclairLecture()) {
+ GameState.setWSCSeenSinclairLecture(true);
+ startExtraSequence(kW65SouthSinclairLecture, kExtraCompletedFlag, kFilterAllInput);
+ }
+ break;
+ case MakeRoomView(kWSC66, kWest):
+ case MakeRoomView(kWSC67, kEast):
+ if (!GameState.getWSCHeardPage2()) {
+ playSpotSoundSync(kPaging2In, kPaging2Out);
+ GameState.setWSCHeardPage2(true);
+ }
+ case MakeRoomView(kWSC10, kNorth):
+ case MakeRoomView(kWSC26, kSouth):
+ case MakeRoomView(kWSC72, kWest):
+ case MakeRoomView(kWSC83, kWest):
+ if (!GameState.getWSCHeardCheckIn()) {
+ playSpotSoundSync(kCheckInIn, kCheckInOut);
+ GameState.setWSCHeardCheckIn(true);
+ }
+ break;
+ case MakeRoomView(kWSC0Z, kSouth):
+ if (getCurrentAlternate() == kAltWSCW0ZDoorOpen)
+ turnLeft();
+ break;
+ case MakeRoomView(kWSC93, kEast):
+ GameState.setWSCBeenAtWSC93(true);
+ break;
+ case MakeRoomView(kWSC98, kWest):
+ if (!GameState.getWSCRobotDead()) {
+ scheduleEvent(kGawkAtRobotTime2, 1, kTimerEventPlayerGawkingAtRobot2);
+ setCurrentActivation(kActivationRobotTurning);
+ if (g_AIArea)
+ g_AIArea->checkMiddleArea();
+ } else if (!GameState.getWSCRobotGone()) {
+ setCurrentActivation(kActivationRobotDead);
+ } else {
+ if (GameState.getWSCCatwalkDark()) {
+ // Change the gun hot spot...
+ _vm->getAllHotspots().setHotspotRect(kW98StunGunSpotID, Common::Rect(181 + kNavAreaLeft,
+ 99 + kNavAreaTop,372 + kNavAreaLeft, 149 + kNavAreaTop));
+ }
+ setCurrentActivation(kActivationRobotGone);
+ }
+ break;
+ case MakeRoomView(kWSCDeathRoom, kNorth):
+ case MakeRoomView(kWSCDeathRoom, kSouth):
+ case MakeRoomView(kWSCDeathRoom, kEast):
+ case MakeRoomView(kWSCDeathRoom, kWest):
+ die(kDeathArrestedInWSC);
+ break;
+ }
+
+ checkPeopleCrossing();
+ setUpPoison();
+}
+
+void WSC::turnTo(const DirectionConstant direction) {
+ Neighborhood::turnTo(direction);
+
+ switch (MakeRoomView(GameState.getCurrentRoom(), direction)) {
+ case MakeRoomView(kWSC01, kNorth):
+ case MakeRoomView(kWSC01, kSouth):
+ GameState.setWSCAnalyzerOn(false);
+ break;
+ case MakeRoomView(kWSC03, kNorth):
+ setCurrentActivation(kActivationW03NorthOff);
+ break;
+ case MakeRoomView(kWSC03, kSouth):
+ if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote())
+ setCurrentActivation(kActivationReadyForSynthesis);
+ break;
+ case MakeRoomView(kWSC07, kSouth):
+ case MakeRoomView(kWSC56, kNorth):
+ setCurrentActivation(kActivationReadyForMap);
+ break;
+ case MakeRoomView(kWSC18, kSouth):
+ case MakeRoomView(kWSC57, kEast):
+ case MakeRoomView(kWSC75, kEast):
+ case MakeRoomView(kWSC90, kSouth):
+ if (!GameState.getWSCHeardCheckIn()) {
+ playSpotSoundSync(kCheckInIn, kCheckInOut);
+ GameState.setWSCHeardCheckIn(true);
+ }
+ break;
+ case MakeRoomView(kWSC56, kSouth):
+ if (!GameState.getWSCHeardPage1()) {
+ playSpotSoundSync(kPaging1In, kPaging1Out);
+ GameState.setWSCHeardPage1(true);
+ }
+ // clone2727 says: This falls through?!??! WTF?
+ case MakeRoomView(kWSC42, kEast):
+ _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false);
+ setCurrentActivation(kActivationSinclairOfficeLocked);
+ break;
+ case MakeRoomView(kWSC58, kSouth):
+ setCurrentActivation(kActivationW58SouthDoorLocked);
+ _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false);
+ break;
+ case MakeRoomView(kWSC73, kWest):
+ setCurrentAlternate(kAltWSCNormal);
+ break;
+ case MakeRoomView(kWSC0Z, kEast):
+ if (getCurrentAlternate() == kAltWSCW0ZDoorOpen)
+ startExtraSequence(kW0ZSpottedByWomen, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+
+ checkPeopleCrossing();
+}
+
+void WSC::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ int32 currentEnergy;
+ Item *item;
+
+ if (flags & kExtraCompletedFlag) {
+ _interruptionFilter = kFilterAllInput;
+
+ switch (_lastExtra) {
+ case kWSCArrivalFromTSA:
+ GameState.setWSCSeenTimeStream(true);
+ loadAmbientLoops();
+ break;
+ case kWSCDartScan1:
+ setCurrentActivation(kActivationShotByRobot);
+ GameState.setWSCPoisoned(true);
+ setUpPoison();
+ makeContinuePoint();
+ break;
+ case kWSCDartScan2:
+ _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kPoisonDart));
+ GameState.setScoringRemovedDart();
+ GameState.setWSCRemovedDart(true);
+ setUpPoison();
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XW1WB2", false, kHintInterruption);
+ // Fall through...
+ case kWSCDartScanNo:
+ GameState.setWSCAnsweredAboutDart(true);
+ startExtraSequence(kWSCDartScan3, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kWSCDartScan3:
+ setCurrentActivation(kActivateHotSpotAlways);
+ break;
+ case kWSCAnalyzerPowerUp:
+ case kWSCAnalyzerPowerUpWithDart:
+ GameState.setWSCAnalyzerOn(true);
+ break;
+ case kWSCDropDartIntoAnalyzer:
+ setCurrentActivation(kActivationZoomedInToAnalyzer);
+ break;
+ case kWSCAnalyzeDart:
+ GameState.setWSCAnalyzedDart(true);
+ GameState.setScoringAnalyzedDart();
+ break;
+ case kWSCZoomOutFromAnalyzer:
+ setCurrentActivation(kActivateHotSpotAlways);
+ GameState.setWSCAnalyzerOn(false);
+ GameState.setWSCDartInAnalyzer(false);
+ updateViewFrame();
+ break;
+ case kMessagesMovedToOffice:
+ case kMessagesMovedToOfficeNoNitrogen:
+ _privateFlags.setFlag(kWSCPrivateLabMessagesOpenFlag, true);
+ GameState.setScoringPlayedWithMessages();
+ break;
+ case kMessagesOff:
+ case kMessagesOffNoNitrogen:
+ _privateFlags.setFlag(kWSCPrivateLabMessagesOpenFlag, false);
+ if (_cachedZoomSpot) {
+ zoomTo(_cachedZoomSpot);
+ _cachedZoomSpot = 0;
+ }
+ break;
+ case kWSC02TurnOnMorphScreen:
+ setCurrentActivation(kActivationReadyForMorph);
+ break;
+ case kWSC02DropToMorphExperiment:
+ loopExtraSequence(kWSC02MorphLoop, kExtraCompletedFlag);
+ setCurrentActivation(kActivationMorphLooping);
+ break;
+ case kWSC02MorphLoop:
+ if (_privateFlags.getFlag(kWSCPrivateInterruptedMorphFlag))
+ startExtraSequence(kWSC02MorphInterruption, kExtraCompletedFlag, kFilterNoInput);
+ else
+ scheduleNavCallBack(kExtraCompletedFlag);
+ break;
+ case kWSC02MorphInterruption:
+ setCurrentActivation(kActivationMorphInterrupted);
+ GameState.setScoringSawMorphExperiment();
+ break;
+ case kWSC02TurnOffMorphScreen:
+ setCurrentActivation(kActivationMorphScreenOff);
+ GameState.setWSCSawMorph(true);
+ break;
+ case kW03NorthActivate:
+ if (GameState.getWSCAnalyzedDart() && !GameState.getWSCDesignedAntidote())
+ startExtraSequence(kW03NorthGetData, kExtraCompletedFlag, kFilterNoInput);
+ else
+ setCurrentActivation(kActivateHotSpotAlways);
+ break;
+ case kW03NorthGetData:
+ setCurrentActivation(kActivationW03NorthReadyForInstructions);
+ break;
+ case kW03NorthInstructions:
+ setCurrentActivation(kActivationW03NorthSawInstructions);
+ break;
+ case kW03NorthPrepMolecule1:
+ setUpMoleculeGame();
+ break;
+ case kW03NorthPrepMolecule2:
+ case kW03NorthPrepMolecule3:
+ nextMoleculeGameLevel();
+ break;
+ case kW03NorthFinishSynthesis:
+ setCurrentActivation(kActivateHotSpotAlways);
+ _privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, false);
+ GameState.setWSCDesignedAntidote(true);
+ GameState.setScoringBuiltAntidote();
+ break;
+ case kW03SouthCreateAntidote:
+ setCurrentActivation(kActivationSynthesizerLooping);
+ loopExtraSequence(kW03SouthAntidoteLoop);
+ break;
+ case kW03SouthDeactivate:
+ setCurrentActivation(kActivateHotSpotAlways);
+ break;
+ case kWSC07SouthMap:
+ case kWSC56SouthMap:
+ setCurrentActivation(kActivateHotSpotAlways);
+ GameState.setScoringSawWSCDirectory();
+ break;
+ case kNerdAtTheDoor1:
+ GameState.setWSCSeenNerd(true);
+ break;
+ case kNerdAtTheDoor2:
+ die(kDeathArrestedInWSC);
+ break;
+ case kW61Brochure:
+ GameState.setScoringSawBrochure();
+ loadAmbientLoops();
+ break;
+ case kW61SouthSmartAlloysWithGun:
+ case kW61SouthSmartAlloysNoGun:
+ GameState.setScoringSawSinclairEntry1();
+ break;
+ case kW61SouthMorphingWithGun:
+ case kW61SouthMorphingNoGun:
+ GameState.setScoringSawSinclairEntry2();
+ break;
+ case kW61SouthTimeBendingWithGun:
+ case kW61SouthTimeBendingNoGun:
+ GameState.setScoringSawSinclairEntry3();
+ break;
+ case kW61MessagesOn:
+ GameState.setWSCOfficeMessagesOpen(true);
+ setCurrentActivation(kActivationW61MessagesOn);
+ break;
+ case kW61MessagesOff:
+ GameState.setWSCOfficeMessagesOpen(false);
+ setCurrentActivation(kActivationW61MessagesOff);
+ if (_cachedZoomSpot) {
+ zoomTo(_cachedZoomSpot);
+ _cachedZoomSpot = 0;
+ }
+ break;
+ case kW61SouthScreenOnWithGun:
+ case kW61SouthScreenOnNoGun:
+ _privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, true);
+ setCurrentActivation(kActivationW61SouthOn);
+ break;
+ case kW61SouthScreenOffWithGun:
+ case kW61SouthScreenOffNoGun:
+ _privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, false);
+ setCurrentActivation(kActivationW61SouthOff);
+ if (_cachedZoomSpot) {
+ zoomTo(_cachedZoomSpot);
+ _cachedZoomSpot = 0;
+ }
+ break;
+ case kW62ZoomOutFromRobot:
+ // Handle action queue before starting new movie sequences.
+ Neighborhood::receiveNotification(notification, flags);
+ _energyDrainRate = g_energyMonitor->getEnergyDrainRate();
+ g_energyMonitor->setEnergyDrainRate(0);
+ currentEnergy = g_energyMonitor->getCurrentEnergy();
+ _vm->setEnergyDeathReason(kDeathHitByPlasma);
+
+ if (GameState.getShieldOn())
+ currentEnergy -= kPlasmaEnergyWithShield;
+ else
+ currentEnergy -= kPlasmaEnergyNoShield;
+
+ if (currentEnergy <= 0)
+ startExtraSequence(kW62PlasmaDodgeDie, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kW62PlasmaDodgeSurvive, kExtraCompletedFlag, kFilterNoInput);
+
+ scheduleEvent(kPlasmaImpactTime, kOneTickPerSecond, kTimerEventPlasmaHit);
+ break;
+ case kW62PlasmaDodgeDie:
+ g_energyMonitor->setEnergyValue(0);
+ break;
+ case kW62PlasmaDodgeSurvive:
+ if (GameState.getShieldOn()) {
+ g_shield->setItemState(kShieldNormal);
+ g_energyMonitor->drainEnergy(kPlasmaEnergyWithShield);
+ } else {
+ g_energyMonitor->drainEnergy(kPlasmaEnergyNoShield);
+ }
+
+ g_energyMonitor->setEnergyDrainRate(_energyDrainRate);
+ g_AIArea->unlockAI();
+ GameState.setScoringFinishedPlasmaDodge();
+ GameState.setWSCDidPlasmaDodge(true);
+ restoreStriding(kWSC58, kSouth, kAltWSCNormal);
+ loadAmbientLoops();
+ break;
+ case kW0ZSpottedByWomen:
+ die(kDeathArrestedInWSC);
+ break;
+ case kW17WestPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt17WestFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, false);
+ break;
+ case kW21SouthPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt21SouthFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, true);
+ break;
+ case kW24SouthPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt24SouthFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, true);
+ break;
+ case kW34EastPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt34EastFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, true);
+ break;
+ case kW36WestPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt36WestFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, true);
+ break;
+ case kW38NorthPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt38NorthFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, true);
+ break;
+ case kW46SouthPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt46SouthFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, true);
+ break;
+ case kW49NorthPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt49NorthFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, false);
+ break;
+ case kW73WestPeopleCrossing:
+ _privateFlags.setFlag(kWSCPrivateSeenPeopleAt73WestFlag, true);
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, false);
+ break;
+ case kW95RobotShoots:
+ case kW98RobotShoots:
+ die(kDeathShotOnCatwalk);
+ break;
+ case kW98MorphsToRobot:
+ if (_argonSprite) {
+ delete _argonSprite; _argonSprite = 0;
+ startExtraSequence(kW98RobotGassed, kExtraCompletedFlag, kFilterNoInput);
+ } else if (_privateFlags.getFlag(kWSCPrivateClickedCatwalkCableFlag)) {
+ startExtraSequence(kW98RobotShocked, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ startExtraSequence(kW98RobotShoots, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kW98RobotShocked:
+ GameState.setWSCCatwalkDark(true);
+ // Change the gun hot spot...
+ _vm->getAllHotspots().setHotspotRect(kW98StunGunSpotID, Common::Rect(181 + kNavAreaLeft, 99 + kNavAreaTop,
+ 372 + kNavAreaLeft, 149 + kNavAreaTop));
+ setCurrentActivation(kActivationRobotDead);
+ GameState.setWSCRobotDead(true);
+ GameState.setScoringStoppedWSCRobot();
+
+ // Video is not present
+ //g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption);
+ break;
+ case kW98RobotGassed:
+ item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister);
+ _vm->addItemToInventory((InventoryItem *)item);
+ setCurrentActivation(kActivationRobotDead);
+ GameState.setWSCRobotDead(true);
+ GameState.setScoringStoppedWSCRobot();
+
+ // Video is not present
+ //g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption);
+ break;
+ case kW98RobotHeadOpensLight:
+ case kW98RobotHeadOpensDark:
+ setCurrentActivation(kActivationWSCRobotHeadOpen);
+ _privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, true);
+ break;
+ case kW98RobotHeadClosesDark:
+ case kW98RobotHeadClosesLight:
+ setCurrentActivation(kActivationRobotGone);
+ _privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, false);
+ GameState.setWSCRobotGone(true);
+ break;
+ }
+ }
+
+ Neighborhood::receiveNotification(notification, flags);
+ g_AIArea->checkMiddleArea();
+}
+
+void WSC::timerExpired(const uint32 event) {
+ switch (event) {
+ case kTimerEventPlasmaHit:
+ if (GameState.getShieldOn())
+ g_shield->setItemState(kShieldPlasma);
+ break;
+ case kTimerEventPlayerGawkingAtRobot:
+ startExtraSequence(kW95RobotShoots, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kTimerEventPlayerGawkingAtRobot2:
+ startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ }
+}
+
+void WSC::setUpMoleculeGame() {
+ _privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, true);
+ setCurrentActivation(kActivationW03NorthInGame);
+ initOneMovie(&_moleculesMovie, "Images/World Science Center/Molecules.movie",
+ kWSCMoleculesMovieOrder, kMoleculesMovieLeft, kMoleculesMovieTop, true);
+ _moleculesMovie.redrawMovieWorld();
+ _moleculeBin.initMoleculeBin();
+ _moleculeGameLevel = 0;
+ nextMoleculeGameLevel();
+}
+
+void WSC::nextMoleculeGameLevel() {
+ _moleculeGameLevel++;
+
+ for (byte i = 0; i < 6; ++i)
+ _levelArray[i] = i;
+
+ _vm->shuffleArray((int32 *)_levelArray, 6);
+ _moleculeBin.setBinLayout(_levelArray);
+ startMoleculeGameLevel();
+}
+
+void WSC::startMoleculeGameLevel() {
+ _moleculeBin.resetBin();
+ _numCorrect = 0;
+ _moleculesMovie.stop();
+ _moleculesMovie.setFlags(0);
+ _moleculesMovie.setSegment(s_moleculeLoopTimes[0], s_moleculeLoopTimes[0] + kMoleculeLoopTime);
+ _moleculesMovie.setTime(s_moleculeLoopTimes[0]);
+ _moleculesMovie.setFlags(kLoopTimeBase);
+ _moleculesMovie.show();
+
+ switch (_moleculeGameLevel) {
+ case 1:
+ playSpotSoundSync(kWSCMolecule1In, kWSCMolecule1Out);
+ break;
+ case 2:
+ playSpotSoundSync(kWSCMolecule2In, kWSCMolecule2Out);
+ break;
+ case 3:
+ playSpotSoundSync(kWSCMolecule3In, kWSCMolecule3Out);
+ break;
+ }
+
+ _moleculesMovie.start();
+}
+
+void WSC::moleculeGameClick(const HotSpotID id) {
+ uint32 molecule = id - kWSC03NorthMolecule1SpotID;
+
+ _moleculeBin.highlightMolecule(molecule);
+ _moleculeBin.selectMolecule(molecule);
+
+ if (molecule == _levelArray[_numCorrect]) {
+ playSpotSoundSync(kWSCClick2In, kWSCClick2Out);
+ _numCorrect++;
+ _moleculesMovie.stop();
+ _moleculesMovie.setFlags(0);
+
+ TimeValue time = _moleculesMovie.getTime();
+ _moleculesMovie.setSegment(s_moleculeLoopTimes[_numCorrect], s_moleculeLoopTimes[_numCorrect] + kMoleculeLoopTime);
+ _moleculesMovie.setTime(s_moleculeLoopTimes[_numCorrect] + time - s_moleculeLoopTimes[_numCorrect - 1]);
+
+ if (_numCorrect == 6) {
+ _moleculesMovie.start();
+
+ while (_moleculesMovie.isRunning()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+
+ _moleculesMovie.stop();
+ _moleculesMovie.hide();
+
+ switch (_moleculeGameLevel) {
+ case 1:
+ startExtraSequence(kW03NorthPrepMolecule2, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 2:
+ startExtraSequence(kW03NorthPrepMolecule3, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case 3:
+ _moleculesMovie.releaseMovie();
+ _moleculeBin.cleanUpMoleculeBin();
+ requestExtraSequence(kW03NorthFinishSynthesis, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ } else {
+ _moleculesMovie.setFlags(kLoopTimeBase);
+ _moleculesMovie.start();
+ }
+ } else {
+ // FAIL
+ playSpotSoundSync(kWSCClick3In, kWSCClick3Out);
+
+ _moleculesMovie.stop();
+ _moleculesMovie.setFlags(0);
+ _moleculesMovie.start();
+
+ while (_moleculesMovie.isRunning()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+
+ _moleculesMovie.stop();
+ _moleculesMovie.setFlags(0);
+ _moleculesMovie.setSegment(s_moleculeFailTimes[_numCorrect], s_moleculeFailTimes[_numCorrect] + kMoleculeFailTime);
+ _moleculesMovie.setTime(s_moleculeFailTimes[_numCorrect]);
+ _moleculesMovie.start();
+
+
+ while (_moleculesMovie.isRunning()) {
+ _vm->checkCallBacks();
+ _vm->refreshDisplay();
+ _vm->_system->delayMillis(10);
+ }
+
+ _moleculesMovie.stop();
+ startMoleculeGameLevel();
+ }
+}
+
+void WSC::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) {
+ Neighborhood::activateOneHotspot(entry, hotspot);
+
+ Item *argonCanister;
+
+ switch (hotspot->getObjectID()) {
+ case kWSCTurnOnAnalyzerSpotID:
+ if (GameState.getWSCAnalyzerOn())
+ hotspot->setInactive();
+ break;
+ case kWSC02SouthTakeArgonSpotID:
+ if (!GameState.getWSCSawMorph() || GameState.isTakenItemID(kArgonCanister))
+ hotspot->setInactive();
+ break;
+ case kWSC02ActivateMorphScreenSpotID:
+ if (GameState.getWSCSawMorph())
+ hotspot->setInactive();
+ break;
+ case kWSC03NorthMolecule1SpotID:
+ case kWSC03NorthMolecule2SpotID:
+ case kWSC03NorthMolecule3SpotID:
+ case kWSC03NorthMolecule4SpotID:
+ case kWSC03NorthMolecule5SpotID:
+ case kWSC03NorthMolecule6SpotID:
+ if (_moleculeBin.isMoleculeHighlighted(hotspot->getObjectID() - kWSC03NorthMolecule1SpotID))
+ hotspot->setInactive();
+ break;
+ case kWSC03SouthPickUpAntidoteSpotID:
+ if (getCurrentActivation() == kActivationSynthesizerLooping)
+ hotspot->setActive();
+ break;
+ case kW98DropArgonSpotID:
+ argonCanister = _vm->getAllItems().findItemByID(kArgonCanister);
+ if (argonCanister->getItemState() != kArgonFull)
+ hotspot->setInactive();
+ break;
+ }
+}
+
+void WSC::activateHotspots() {
+ Neighborhood::activateHotspots();
+
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC98, kWest) && _privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) {
+ if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag))
+ _vm->getAllHotspots().deactivateOneHotspot(kW98RetinalChipSpotID);
+ else
+ _vm->getAllHotspots().activateOneHotspot(kW98RetinalChipSpotID);
+
+ if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag))
+ _vm->getAllHotspots().deactivateOneHotspot(kW98MapChipSpotID);
+ else
+ _vm->getAllHotspots().activateOneHotspot(kW98MapChipSpotID);
+
+ if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag))
+ _vm->getAllHotspots().deactivateOneHotspot(kW98OpticalChipSpotID);
+ else
+ _vm->getAllHotspots().activateOneHotspot(kW98OpticalChipSpotID);
+ }
+}
+
+void WSC::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+ if (JMPPPInput::isEasterEggModifierInput(input))
+ GameState.setEasterEgg(true);
+
+ if (clickedSpot) {
+ switch (clickedSpot->getObjectID()) {
+ case kWSCAnalyzerScreenSpotID:
+ requestExtraSequence(kWSCAnalyzeDart, kExtraCompletedFlag, kFilterNoInput);
+ requestExtraSequence(kWSCZoomOutFromAnalyzer, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kWSC02SouthPlayMessagesSpotID:
+ if (GameState.isTakenItemID(kNitrogenCanister)) {
+ if (_lastExtra == (uint32)kMessagesMovedToOfficeNoNitrogen)
+ startExtraSequence(kMessagesOffNoNitrogen, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMessagesMovedToOfficeNoNitrogen, kExtraCompletedFlag, kFilterNoInput);
+ } else {
+ if (_lastExtra == (uint32)kMessagesMovedToOffice)
+ startExtraSequence(kMessagesOff, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kMessagesMovedToOffice, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kWSC02SouthInterruptMorphSpotID:
+ _privateFlags.setFlag(kWSCPrivateInterruptedMorphFlag, true);
+ break;
+ case kWSC02SouthMorphFinishedSpotID:
+ requestExtraSequence(kWSC02MorphFinished, 0, kFilterNoInput);
+ requestExtraSequence(kWSC02TurnOffMorphScreen, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kWSC03NorthMolecule1SpotID:
+ case kWSC03NorthMolecule2SpotID:
+ case kWSC03NorthMolecule3SpotID:
+ case kWSC03NorthMolecule4SpotID:
+ case kWSC03NorthMolecule5SpotID:
+ case kWSC03NorthMolecule6SpotID:
+ moleculeGameClick(clickedSpot->getObjectID());
+ break;
+ case kW98GrabCableSpotID:
+ if (isEventTimerRunning()) {
+ cancelEvent();
+ startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput);
+ }
+
+ _privateFlags.setFlag(kWSCPrivateClickedCatwalkCableFlag, true);
+ break;
+ default:
+ Neighborhood::clickInHotspot(input, clickedSpot);
+ break;
+ }
+ } else {
+ Neighborhood::clickInHotspot(input, clickedSpot);
+ }
+
+ GameState.setEasterEgg(false);
+}
+
+void WSC::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
+ CoordType h, v;
+
+ switch (item->getObjectID()) {
+ case kPoisonDart:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ GameState.setWSCDartInAnalyzer(true);
+ if (dropSpot && dropSpot->getObjectID() == kWSCDropDartSpotID) {
+ if (!GameState.getWSCAnalyzerOn())
+ requestExtraSequence(kWSCAnalyzerPowerUpWithDart, kExtraCompletedFlag, kFilterNoInput);
+
+ requestExtraSequence(kWSCDropDartIntoAnalyzer, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kAntidote:
+ _privateFlags.setFlag(kWSCDraggingAntidoteFlag, false);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ loopExtraSequence(kW03SouthAntidoteLoop);
+ break;
+ case kSinclairKey:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, true);
+ openDoor();
+ break;
+ case kCrowbar:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, true);
+ openDoor();
+ break;
+ case kMachineGun:
+ setCurrentAlternate(kAltWSCNormal);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ case kArgonCanister:
+ item->setItemState(kArgonEmpty);
+ _argonSprite = item->getDragSprite(0);
+ _argonSprite->setCurrentFrameIndex(1);
+ _argonSprite->setDisplayOrder(kDragSpriteOrder);
+ dropSpot->getCenter(h, v);
+ _argonSprite->centerElementAt(h, v);
+ _argonSprite->startDisplaying();
+ _argonSprite->show();
+
+ if (isEventTimerRunning()) {
+ cancelEvent();
+ startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput);
+ }
+ break;
+ case kRetinalScanBiochip:
+ _privateFlags.setFlag(kWSCPrivateGotRetScanChipFlag, false);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ case kMapBiochip:
+ _privateFlags.setFlag(kWSCPrivateGotMapChipFlag, false);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ case kOpticalBiochip:
+ _privateFlags.setFlag(kWSCPrivateGotOpticalChipFlag, false);
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ default:
+ Neighborhood::dropItemIntoRoom(item, dropSpot);
+ break;
+ }
+}
+
+void WSC::takeItemFromRoom(Item *item) {
+ switch (item->getObjectID()) {
+ case kAntidote:
+ _privateFlags.setFlag(kWSCDraggingAntidoteFlag, true);
+ Neighborhood::takeItemFromRoom(item);
+ break;
+ case kMachineGun:
+ setCurrentAlternate(kAltWSCTookMachineGun);
+ Neighborhood::takeItemFromRoom(item);
+ break;
+ case kRetinalScanBiochip:
+ _privateFlags.setFlag(kWSCPrivateGotRetScanChipFlag, true);
+ Neighborhood::takeItemFromRoom(item);
+ break;
+ case kMapBiochip:
+ _privateFlags.setFlag(kWSCPrivateGotMapChipFlag, true);
+ Neighborhood::takeItemFromRoom(item);
+ break;
+ case kOpticalBiochip:
+ _privateFlags.setFlag(kWSCPrivateGotOpticalChipFlag, true);
+ Neighborhood::takeItemFromRoom(item);
+ break;
+ default:
+ Neighborhood::takeItemFromRoom(item);
+ break;
+ }
+}
+
+Hotspot *WSC::getItemScreenSpot(Item *item, DisplayElement *element) {
+ HotSpotID destSpotID;
+
+ switch (item->getObjectID()) {
+ case kNitrogenCanister:
+ destSpotID = kWSC02SouthTakeNitrogenSpotID;
+ break;
+ case kArgonPickup:
+ destSpotID = kWSC02SouthTakeArgonSpotID;
+ break;
+ case kAntidote:
+ destSpotID = kWSC03SouthPickUpAntidoteSpotID;
+ break;
+ case kMachineGun:
+ destSpotID = kW61SouthMachineGunSpotID;
+ break;
+ case kRetinalScanBiochip:
+ destSpotID = kW98RetinalChipSpotID;
+ break;
+ case kMapBiochip:
+ destSpotID = kW98MapChipSpotID;
+ break;
+ case kOpticalBiochip:
+ destSpotID = kW98OpticalChipSpotID;
+ break;
+ default:
+ destSpotID = kNoHotSpotID;
+ break;
+ }
+
+ if (destSpotID == kNoHotSpotID)
+ return Neighborhood::getItemScreenSpot(item, element);
+
+ return _vm->getAllHotspots().findHotspotByID(destSpotID);
+}
+
+void WSC::pickedUpItem(Item *item) {
+ switch (item->getObjectID()) {
+ case kAntidote:
+ if (!GameState.getWSCPickedUpAntidote()) {
+ GameState.setWSCPoisoned(false);
+ GameState.setWSCRemovedDart(false);
+ GameState.setWSCPickedUpAntidote(true);
+ _privateFlags.setFlag(kWSCDraggingAntidoteFlag, false);
+ playSpotSoundSync(kDrinkAntidoteIn, kDrinkAntidoteOut);
+ setUpPoison();
+ startExtraSequence(kW03SouthDeactivate, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kArgonPickup:
+ _vm->removeItemFromInventory((InventoryItem *)item);
+ item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister);
+ _vm->addItemToInventory((InventoryItem *)item);
+ item = (Item *)_vm->getAllItems().findItemByID(kSinclairKey);
+ _vm->addItemToInventory((InventoryItem *)item);
+ _vm->getAllHotspots().setHotspotRect(kWSC02SouthMorphOutSpotID,
+ Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop));
+ break;
+ case kArgonCanister:
+ GameState.setScoringGotArgonCanister();
+ break;
+ case kSinclairKey:
+ GameState.setScoringGotSinclairKey();
+ break;
+ case kNitrogenCanister:
+ GameState.setScoringGotNitrogenCanister();
+ break;
+ case kRetinalScanBiochip:
+ if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag) && _privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) {
+ if (GameState.getWSCCatwalkDark())
+ startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kMapBiochip:
+ if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag) && _privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) {
+ if (GameState.getWSCCatwalkDark())
+ startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kOpticalBiochip:
+ g_opticalChip->addMercury();
+ GameState.setScoringGotWSCOpMemChip();
+ if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag) && _privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) {
+ if (GameState.getWSCCatwalkDark())
+ startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kStunGun:
+ GameState.setWSCFinished(true);
+
+ if (!GameState.getWSCCatwalkDark())
+ GameState.setScoringWSCGandhi();
+
+ recallToTSASuccess();
+ break;
+ }
+}
+
+void WSC::checkPeopleCrossing() {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kWSC17, kWest):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt17WestFlag))
+ startExtraSequence(kW17WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC21, kSouth):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt21SouthFlag))
+ startExtraSequence(kW21SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC24, kSouth):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt24SouthFlag))
+ startExtraSequence(kW24SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC34, kEast):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt34EastFlag))
+ startExtraSequence(kW34EastPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC36, kWest):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt36WestFlag))
+ startExtraSequence(kW36WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC38, kNorth):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt38NorthFlag))
+ startExtraSequence(kW38NorthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC46, kSouth):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt46SouthFlag))
+ startExtraSequence(kW46SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC49, kNorth):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt49NorthFlag))
+ startExtraSequence(kW49NorthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case MakeRoomView(kWSC73, kWest):
+ if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt73WestFlag))
+ startExtraSequence(kW73WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt21SouthFlag) && _vm->getRandomNumber(2) == 0) {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, true);
+ forceStridingStop(kWSC18, kSouth, kAltWSCNormal);
+ } else {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, false);
+ restoreStriding(kWSC18, kSouth, kAltWSCNormal);
+ }
+
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt19NorthFlag) && _vm->getRandomNumber(2) == 0) {
+ forceStridingStop(kWSC22, kNorth, kAltWSCNormal);
+ } else {
+ restoreStriding(kWSC22, kNorth, kAltWSCNormal);
+ }
+
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt24SouthFlag) && _vm->getRandomNumber(2) == 0) {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, true);
+ forceStridingStop(kWSC22, kSouth, kAltWSCNormal);
+ } else {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, false);
+ restoreStriding(kWSC22, kSouth, kAltWSCNormal);
+ }
+
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt34EastFlag) && _vm->getRandomNumber(2) == 0) {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, true);
+ forceStridingStop(kWSC28, kEast, kAltWSCNormal);
+ } else {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, false);
+ restoreStriding(kWSC28, kEast, kAltWSCNormal);
+ }
+
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt36WestFlag) && _vm->getRandomNumber(2) == 0) {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, true);
+ forceStridingStop(kWSC40, kWest, kAltWSCNormal);
+ } else {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, false);
+ restoreStriding(kWSC40, kWest, kAltWSCNormal);
+ }
+
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt38NorthFlag) && _vm->getRandomNumber(2) == 0) {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, true);
+ forceStridingStop(kWSC42, kNorth, kAltWSCNormal);
+ } else {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, false);
+ restoreStriding(kWSC42, kNorth, kAltWSCNormal);
+ }
+
+ if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt46SouthFlag) && _vm->getRandomNumber(2) == 0) {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, true);
+ forceStridingStop(kWSC44, kSouth, kAltWSCNormal);
+ } else {
+ _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, false);
+ restoreStriding(kWSC44, kSouth, kAltWSCNormal);
+ }
+ break;
+ }
+}
+
+void WSC::setUpPoison() {
+ if (GameState.getWSCPoisoned()) {
+ if (GameState.getWSCRemovedDart()) {
+ if (g_energyMonitor->getEnergyDrainRate() != kWSCPoisonEnergyDrainNoDart) {
+ g_energyMonitor->setEnergyDrainRate(kWSCPoisonEnergyDrainNoDart);
+ _vm->setEnergyDeathReason(kDeathDidntStopPoison);
+ }
+ } else {
+ if (g_energyMonitor->getEnergyDrainRate() != kWSCPoisonEnergyDrainWithDart) {
+ g_energyMonitor->setEnergyDrainRate(kWSCPoisonEnergyDrainWithDart);
+ _vm->setEnergyDeathReason(kDeathDidntStopPoison);
+ }
+ }
+ } else if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) {
+ g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal);
+ _vm->resetEnergyDeathReason();
+ }
+}
+
+bool WSC::inSynthesizerGame() {
+ return _moleculesMovie.isMovieValid();
+}
+
+bool WSC::canSolve() {
+ return (inSynthesizerGame() || (GameState.getCurrentRoom() == kWSC98 && !GameState.getWSCRobotDead()));
+}
+
+void WSC::doSolve() {
+ if (inSynthesizerGame()) {
+ _moleculesMovie.releaseMovie();
+ _moleculeBin.cleanUpMoleculeBin();
+ requestExtraSequence(kW03NorthFinishSynthesis, kExtraCompletedFlag, kFilterNoInput);
+ } else if (GameState.getCurrentRoom() == kWSC98 && !GameState.getWSCRobotDead()) {
+ cancelEvent();
+ startExtraSequence(kW98RobotShocked, kExtraCompletedFlag, kFilterNoInput);
+ }
+}
+
+Common::String WSC::getNavMovieName() {
+ return "Images/World Science Center/WSC.movie";
+}
+
+Common::String WSC::getSoundSpotsName() {
+ return "Sounds/World Science Center/WSC Spots";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/wsc/wsc.h b/engines/pegasus/neighborhood/wsc/wsc.h
new file mode 100644
index 0000000000..d9634b3539
--- /dev/null
+++ b/engines/pegasus/neighborhood/wsc/wsc.h
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_WSC_WSC_H
+#define PEGASUS_NEIGHBORHOOD_WSC_WSC_H
+
+#include "pegasus/neighborhood/neighborhood.h"
+#include "pegasus/neighborhood/wsc/moleculebin.h"
+
+namespace Pegasus {
+
+static const DisplayOrder kWSCMoleculeBinOrder = kMonitorLayer;
+static const DisplayOrder kWSCMoleculesMovieOrder = kWSCMoleculeBinOrder + 1;
+
+static const RoomID kWSC01 = 0;
+static const RoomID kWSC02Morph = 2;
+static const RoomID kWSC02Messages = 3;
+static const RoomID kWSC62 = 62;
+
+class WSC : public Neighborhood {
+public:
+ WSC(InputHandler *, PegasusEngine *);
+ virtual ~WSC() {}
+
+ void flushGameState();
+
+ virtual uint16 getDateResID() const;
+
+ bool okayToJump();
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+ bool inSynthesizerGame();
+
+ bool canSolve();
+ void doSolve();
+
+ virtual void prepareForAIHint(const Common::String &);
+ virtual void cleanUpAfterAIHint(const Common::String &);
+
+ void init();
+ void start();
+
+protected:
+ enum {
+ kWSCDraggingAntidoteFlag,
+
+ kWSCPrivateLabMessagesOpenFlag,
+ kWSCPrivateInterruptedMorphFlag,
+ kWSCPrivateInMoleculeGameFlag,
+ kWSCPrivateSinclairOfficeOpenFlag,
+ kWSCPrivateOfficeLogOpenFlag,
+ kWSCPrivate58SouthOpenFlag,
+ kWSCPrivateClickedCatwalkCableFlag,
+ kWSCPrivateRobotHeadOpenFlag,
+
+ kWSCPrivateSeenPeopleAt17WestFlag,
+ kWSCPrivateSeenPeopleAt19NorthFlag,
+ kWSCPrivateSeenPeopleAt21SouthFlag,
+ kWSCPrivateSeenPeopleAt24SouthFlag,
+ kWSCPrivateSeenPeopleAt34EastFlag,
+ kWSCPrivateSeenPeopleAt36WestFlag,
+ kWSCPrivateSeenPeopleAt38NorthFlag,
+ kWSCPrivateSeenPeopleAt46SouthFlag,
+ kWSCPrivateSeenPeopleAt49NorthFlag,
+ kWSCPrivateSeenPeopleAt73WestFlag,
+
+ kWSCPrivateNeedPeopleAt17WestFlag,
+ kWSCPrivateNeedPeopleAt21SouthFlag,
+ kWSCPrivateNeedPeopleAt24SouthFlag,
+ kWSCPrivateNeedPeopleAt34EastFlag,
+ kWSCPrivateNeedPeopleAt36WestFlag,
+ kWSCPrivateNeedPeopleAt38NorthFlag,
+ kWSCPrivateNeedPeopleAt46SouthFlag,
+ kWSCPrivateNeedPeopleAt49NorthFlag,
+ kWSCPrivateNeedPeopleAt73WestFlag,
+
+ kWSCPrivateGotRetScanChipFlag,
+ kWSCPrivateGotMapChipFlag,
+ kWSCPrivateGotOpticalChipFlag,
+
+ kNumWSCPrivateFlags
+ };
+
+ void arriveAt(const RoomID, const DirectionConstant);
+ void turnTo(const DirectionConstant);
+ void receiveNotification(Notification *, const NotificationFlags);
+ void dropItemIntoRoom(Item *, Hotspot *);
+ void clickInHotspot(const Input &, const Hotspot *);
+ TimeValue getViewTime(const RoomID, const DirectionConstant);
+ void getZoomEntry(const HotSpotID, ZoomTable::Entry &);
+ CanMoveForwardReason canMoveForward(ExitTable::Entry &entry);
+ void cantMoveThatWay(CanMoveForwardReason reason);
+ CanTurnReason canTurn(TurnDirection turn, DirectionConstant &nextDir);
+ void zoomTo(const Hotspot *hotspot);
+ void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *);
+ void setUpMoleculeGame();
+ void nextMoleculeGameLevel();
+ void startMoleculeGameLevel();
+ void moleculeGameClick(const HotSpotID);
+ void loadAmbientLoops();
+ CanOpenDoorReason canOpenDoor(DoorTable::Entry &);
+ void cantOpenDoor(CanOpenDoorReason);
+ void pickedUpItem(Item *);
+ void doorOpened();
+ void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits);
+ void getExtraEntry(const uint32, ExtraTable::Entry &);
+ void takeItemFromRoom(Item *item);
+ void checkPeopleCrossing();
+ void turnLeft();
+ void turnRight();
+ void moveForward();
+ Hotspot *getItemScreenSpot(Item *, DisplayElement *);
+ int16 getStaticCompassAngle(const RoomID, const DirectionConstant);
+ void getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove);
+ void getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove);
+ void bumpIntoWall();
+ void activateHotspots();
+ void setUpAIRules();
+ Common::String getBriefingMovie();
+ Common::String getEnvScanMovie();
+ uint getNumHints();
+ Common::String getHintMovie(uint);
+ void closeDoorOffScreen(const RoomID, const DirectionConstant);
+ void setUpPoison();
+ void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &);
+ void timerExpired(const uint32);
+
+ Common::String getSoundSpotsName();
+ Common::String getNavMovieName();
+
+ FlagsArray<byte, kNumWSCPrivateFlags> _privateFlags;
+ const Hotspot *_cachedZoomSpot;
+ MoleculeBin _moleculeBin;
+ int32 _moleculeGameLevel, _numCorrect;
+ Movie _moleculesMovie;
+ uint32 _levelArray[6];
+ Common::Rational _energyDrainRate;
+ Sprite *_argonSprite;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/zoom.cpp b/engines/pegasus/neighborhood/zoom.cpp
new file mode 100644
index 0000000000..478ec6e493
--- /dev/null
+++ b/engines/pegasus/neighborhood/zoom.cpp
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "pegasus/neighborhood/zoom.h"
+
+namespace Pegasus {
+
+void ZoomTable::loadFromStream(Common::SeekableReadStream *stream) {
+ uint32 count = stream->readUint32BE();
+ _entries.resize(count);
+
+ for (uint32 i = 0; i < count; i++) {
+ _entries[i].hotspot = stream->readUint16BE();
+ _entries[i].movieStart = stream->readUint32BE();
+ _entries[i].movieEnd = stream->readUint32BE();
+ _entries[i].room = stream->readUint16BE();
+ _entries[i].direction = stream->readByte();
+ debug(0, "Zoom[%d]: %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].movieStart,
+ _entries[i].movieEnd, _entries[i].room, _entries[i].direction);
+ stream->readByte(); // alignment
+ }
+}
+
+void ZoomTable::clear() {
+ _entries.clear();
+}
+
+ZoomTable::Entry::Entry() {
+ clear();
+}
+
+void ZoomTable::Entry::clear() {
+ hotspot = kNoHotSpotID;
+ movieStart = 0xffffffff;
+ movieEnd = 0xffffffff;
+ room = kNoRoomID;
+ direction = kNoDirection;
+}
+
+ZoomTable::Entry ZoomTable::findEntry(HotSpotID hotspot) {
+ for (uint32 i = 0; i < _entries.size(); i++)
+ if (_entries[i].hotspot == hotspot)
+ return _entries[i];
+
+ return Entry();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/zoom.h b/engines/pegasus/neighborhood/zoom.h
new file mode 100644
index 0000000000..8bcf8974f8
--- /dev/null
+++ b/engines/pegasus/neighborhood/zoom.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_ZOOM_H
+#define PEGASUS_NEIGHBORHOOD_ZOOM_H
+
+#include "common/array.h"
+#include "common/endian.h"
+
+#include "pegasus/constants.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Pegasus {
+
+class ZoomTable {
+public:
+ ZoomTable() {}
+ ~ZoomTable() {}
+
+ static uint32 getResTag() { return MKTAG('Z', 'o', 'o', 'm'); }
+
+ void loadFromStream(Common::SeekableReadStream *stream);
+ void clear();
+
+ struct Entry {
+ Entry();
+ void clear();
+ bool isEmpty() { return movieStart == 0xffffffff; }
+
+ HotSpotID hotspot;
+ TimeValue movieStart;
+ TimeValue movieEnd;
+ RoomID room;
+ DirectionConstant direction;
+ };
+
+ Entry findEntry(HotSpotID hotspot);
+
+private:
+ Common::Array<Entry> _entries;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/notification.cpp b/engines/pegasus/notification.cpp
new file mode 100644
index 0000000000..2d57fcc5e7
--- /dev/null
+++ b/engines/pegasus/notification.cpp
@@ -0,0 +1,149 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/constants.h"
+#include "pegasus/notification.h"
+
+namespace Pegasus {
+
+typedef ReceiverList::iterator ReceiverIterator;
+
+Notification::Notification(const NotificationID id, NotificationManager *owner) : IDObject(id) {
+ _owner = owner;
+ _currentFlags = kNoNotificationFlags;
+ if (_owner)
+ _owner->addNotification(this);
+}
+
+Notification::~Notification() {
+ for (uint i = 0; i < _receivers.size(); i++)
+ _receivers[i].receiver->newNotification(NULL);
+
+ if (_owner)
+ _owner->removeNotification(this);
+}
+
+// Selectively set or clear notificiation bits.
+// Wherever mask is 0, leave existing bits untouched.
+// Wherever mask is 1, set bit equivalent to flags.
+void Notification::notifyMe(NotificationReceiver *receiver, NotificationFlags flags, NotificationFlags mask) {
+ for (uint i = 0; i < _receivers.size(); i++) {
+ if (_receivers[i].receiver == receiver) {
+ _receivers[i].mask = (_receivers[i].mask & ~mask) | (flags & mask);
+ receiver->newNotification(this);
+ return;
+ }
+ }
+
+ ReceiverEntry newEntry;
+ newEntry.receiver = receiver;
+ newEntry.mask = flags;
+ _receivers.push_back(newEntry);
+
+ receiver->newNotification(this);
+}
+
+void Notification::cancelNotification(NotificationReceiver *receiver) {
+ for (uint i = 0; i < _receivers.size(); i++) {
+ if (_receivers[i].receiver == receiver) {
+ _receivers.remove_at(i);
+ i--;
+ }
+ }
+}
+
+void Notification::setNotificationFlags(NotificationFlags flags, NotificationFlags mask) {
+ _currentFlags = (_currentFlags & ~mask) | flags;
+}
+
+void Notification::checkReceivers() {
+ NotificationFlags currentFlags = _currentFlags;
+ _currentFlags = kNoNotificationFlags;
+
+ for (uint i = 0; i < _receivers.size(); i++)
+ if (_receivers[i].mask & currentFlags)
+ _receivers[i].receiver->receiveNotification(this, currentFlags);
+}
+
+// Receiver entries are equal if their receivers are equal.
+
+int operator==(const ReceiverEntry &entry1, const ReceiverEntry &entry2) {
+ return entry1.receiver == entry2.receiver;
+}
+
+int operator!=(const ReceiverEntry &entry1, const ReceiverEntry &entry2) {
+ return entry1.receiver != entry2.receiver;
+}
+
+NotificationReceiver::NotificationReceiver() {
+ _notification = NULL;
+}
+
+NotificationReceiver::~NotificationReceiver() {
+ if (_notification)
+ _notification->cancelNotification(this);
+}
+
+void NotificationReceiver::receiveNotification(Notification *, const NotificationFlags) {
+}
+
+void NotificationReceiver::newNotification(Notification *notification) {
+ _notification = notification;
+}
+
+typedef NotificationList::iterator NotificationIterator;
+
+NotificationManager::NotificationManager() {
+}
+
+NotificationManager::~NotificationManager() {
+ detachNotifications();
+}
+
+void NotificationManager::addNotification(Notification *notification) {
+ _notifications.push_back(notification);
+}
+
+void NotificationManager::removeNotification(Notification *notification) {
+ for (NotificationIterator it = _notifications.begin(); it != _notifications.end();) {
+ if ((*it) == notification)
+ it = _notifications.erase(it);
+ else
+ it++;
+ }
+}
+
+void NotificationManager::detachNotifications() {
+ for (NotificationIterator it = _notifications.begin(); it != _notifications.end(); it++)
+ (*it)->_owner = 0;
+}
+
+void NotificationManager::checkNotifications() {
+ for (NotificationIterator it = _notifications.begin(); it != _notifications.end(); it++)
+ if ((*it)->_currentFlags != kNoNotificationFlags)
+ (*it)->checkReceivers();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/notification.h b/engines/pegasus/notification.h
new file mode 100644
index 0000000000..19b69829be
--- /dev/null
+++ b/engines/pegasus/notification.h
@@ -0,0 +1,123 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NOTIFICATION_H
+#define PEGASUS_NOTIFICATION_H
+
+#include "common/array.h"
+#include "common/list.h"
+
+#include "pegasus/types.h"
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+class NotificationManager;
+class NotificationReceiver;
+
+struct ReceiverEntry {
+ NotificationReceiver *receiver;
+ NotificationFlags mask;
+};
+
+int operator==(const ReceiverEntry &entry1, const ReceiverEntry &entry2);
+int operator!=(const ReceiverEntry &entry1, const ReceiverEntry &entry2);
+
+typedef Common::Array<ReceiverEntry> ReceiverList;
+
+/*
+ A notification can have 32 flags associated with it, which can be user-defined.
+*/
+
+class Notification : public IDObject {
+friend class NotificationManager;
+
+public:
+ Notification(const NotificationID id, NotificationManager *owner);
+ virtual ~Notification();
+
+ // notifyMe will have this receiver notified when any of the specified notification
+ // flags are set.
+ // If there is already a notification set for this receiver, notifyMe does a bitwise
+ // OR with the receiver's current notification flags.
+
+ // Can selectively set or clear notification bits by using the flags and mask argument.
+
+ void notifyMe(NotificationReceiver*, NotificationFlags flags, NotificationFlags mask);
+ void cancelNotification(NotificationReceiver *receiver);
+
+ void setNotificationFlags(NotificationFlags flags, NotificationFlags mask);
+ NotificationFlags getNotificationFlags() { return _currentFlags; }
+
+ void clearNotificationFlags() { setNotificationFlags(0, ~(NotificationFlags)0); }
+
+protected:
+ void checkReceivers();
+
+ NotificationManager *_owner;
+ ReceiverList _receivers;
+ NotificationFlags _currentFlags;
+};
+
+class NotificationReceiver {
+friend class Notification;
+
+public:
+ NotificationReceiver();
+ virtual ~NotificationReceiver();
+
+protected:
+ // receiveNotification is called automatically whenever a notification that this
+ // receiver depends on has its flags set
+
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+ virtual void newNotification(Notification *notification);
+
+private:
+ Notification *_notification;
+};
+
+typedef Common::List<Notification *> NotificationList;
+
+class NotificationManager : public NotificationReceiver {
+friend class Notification;
+
+public:
+ NotificationManager();
+ virtual ~NotificationManager();
+
+ void checkNotifications();
+
+protected:
+ void addNotification(Notification *notification);
+ void removeNotification(Notification *notification);
+ void detachNotifications();
+
+ NotificationList _notifications;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp
new file mode 100644
index 0000000000..81e8058628
--- /dev/null
+++ b/engines/pegasus/pegasus.cpp
@@ -0,0 +1,2346 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/config-manager.h"
+#include "common/error.h"
+#include "common/events.h"
+#include "common/fs.h"
+#include "common/file.h"
+#include "common/memstream.h"
+#include "common/savefile.h"
+#include "common/textconsole.h"
+#include "common/translation.h"
+#include "common/random.h"
+#include "base/plugins.h"
+#include "base/version.h"
+#include "gui/saveload.h"
+#include "video/qt_decoder.h"
+
+#include "pegasus/console.h"
+#include "pegasus/cursor.h"
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/interface.h"
+#include "pegasus/menu.h"
+#include "pegasus/movie.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/timers.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/itemlist.h"
+#include "pegasus/items/biochips/aichip.h"
+#include "pegasus/items/biochips/biochipitem.h"
+#include "pegasus/items/biochips/mapchip.h"
+#include "pegasus/items/biochips/opticalchip.h"
+#include "pegasus/items/biochips/pegasuschip.h"
+#include "pegasus/items/biochips/retscanchip.h"
+#include "pegasus/items/biochips/shieldchip.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/items/inventory/gascanister.h"
+#include "pegasus/items/inventory/inventoryitem.h"
+#include "pegasus/items/inventory/keycard.h"
+#include "pegasus/neighborhood/neighborhood.h"
+#include "pegasus/neighborhood/caldoria/caldoria.h"
+#include "pegasus/neighborhood/mars/mars.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
+#include "pegasus/neighborhood/norad/delta/noraddelta.h"
+#include "pegasus/neighborhood/prehistoric/prehistoric.h"
+#include "pegasus/neighborhood/tsa/fulltsa.h"
+#include "pegasus/neighborhood/tsa/tinytsa.h"
+#include "pegasus/neighborhood/wsc/wsc.h"
+
+namespace Pegasus {
+
+PegasusEngine::PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc) : Engine(syst), InputHandler(0), _gameDescription(gamedesc),
+ _shellNotification(kJMPDCShellNotificationID, this), _returnHotspot(kInfoReturnSpotID), _itemDragger(this), _bigInfoMovie(kNoDisplayElement),
+ _smallInfoMovie(kNoDisplayElement) {
+ _continuePoint = 0;
+ _saveAllowed = _loadAllowed = true;
+ _saveRequested = _loadRequested = false;
+ _gameMenu = 0;
+ _deathReason = kDeathStranded;
+ _neighborhood = 0;
+ _FXLevel = 0x80;
+ _ambientLevel = 0x80;
+ _gameMode = kNoMode;
+ _switchModesSync = false;
+ _draggingItem = 0;
+ _dragType = kDragNoDrag;
+ _idlerHead = 0;
+ _currentCD = 1;
+ _introTimer = 0;
+ _aiSaveStream = 0;
+}
+
+PegasusEngine::~PegasusEngine() {
+ delete _resFork;
+ delete _console;
+ delete _cursor;
+ delete _continuePoint;
+ delete _gameMenu;
+ delete _neighborhood;
+ delete _rnd;
+ delete _introTimer;
+ delete _aiSaveStream;
+
+ for (ItemIterator it = _allItems.begin(); it != _allItems.end(); it++)
+ delete *it;
+
+ InputDeviceManager::destroy();
+ GameStateManager::destroy();
+
+ // NOTE: This must be deleted last!
+ delete _gfx;
+}
+
+Common::Error PegasusEngine::run() {
+ _console = new PegasusConsole(this);
+ _gfx = new GraphicsManager(this);
+ _resFork = new Common::MacResManager();
+ _cursor = new Cursor();
+ _rnd = new Common::RandomSource("Pegasus");
+
+ if (!_resFork->open("JMP PP Resources") || !_resFork->hasResFork())
+ error("Could not load JMP PP Resources");
+
+ // Initialize items
+ createItems();
+
+ // Initialize cursors
+ _cursor->addCursorFrames(0x80); // Main
+ _cursor->addCursorFrames(900); // Mars Shuttle
+
+ // Initialize the item dragger bounds
+ _itemDragger.setHighlightBounds();
+
+ if (!isDemo() && !detectOpeningClosingDirectory()) {
+ Common::String message = "Missing intro directory. ";
+
+ // Give Mac OS X a more specific message because we can
+#ifdef MACOSX
+ message += "Make sure \"Opening/Closing\" is present.";
+#else
+ message += "Be sure to rename \"Opening/Closing\" to \"Opening_Closing\".";
+#endif
+
+ GUIErrorMessage(message);
+ warning("%s", message.c_str());
+ return Common::kNoGameDataFoundError;
+ }
+
+ // Set up input
+ InputHandler::setInputHandler(this);
+ allowInput(true);
+
+ // Set up inventories
+ _items.setWeightLimit(9);
+ _items.setOwnerID(kPlayerID);
+ _biochips.setWeightLimit(8);
+ _biochips.setOwnerID(kPlayerID);
+
+ _returnHotspot.setArea(Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop));
+ _returnHotspot.setHotspotFlags(kInfoReturnSpotFlag);
+ _allHotspots.push_back(&_returnHotspot);
+
+ _screenDimmer.setBounds(Common::Rect(0, 0, 640, 480));
+ _screenDimmer.setDisplayOrder(kScreenDimmerOrder);
+
+ // Load from the launcher/cli if requested (and don't show the intro in those cases)
+ bool doIntro = true;
+ if (ConfMan.hasKey("save_slot")) {
+ uint32 gameToLoad = ConfMan.getInt("save_slot");
+ doIntro = (loadGameState(gameToLoad).getCode() != Common::kNoError);
+ }
+
+ _shellNotification.notifyMe(this, kJMPShellNotificationFlags, kJMPShellNotificationFlags);
+
+ if (doIntro)
+ // Start up the first notification
+ _shellNotification.setNotificationFlags(kGameStartingFlag, kGameStartingFlag);
+
+ if (!isDemo()) {
+ _introTimer = new FuseFunction();
+ _introTimer->setFunctor(new Common::Functor0Mem<void, PegasusEngine>(this, &PegasusEngine::introTimerExpired));
+ }
+
+ while (!shouldQuit()) {
+ processShell();
+ _system->delayMillis(10); // Ease off the CPU
+ }
+
+ return Common::kNoError;
+}
+
+bool PegasusEngine::canLoadGameStateCurrently() {
+ return _loadAllowed && !isDemo();
+}
+
+bool PegasusEngine::canSaveGameStateCurrently() {
+ return _saveAllowed && !isDemo() && g_neighborhood;
+}
+
+bool PegasusEngine::detectOpeningClosingDirectory() {
+ // We need to detect what our Opening/Closing directory is listed as
+ // On the original disc, it was 'Opening/Closing' but only HFS(+) supports the slash
+ // Mac OS X will display this as 'Opening:Closing' and we can use that directly
+ // On other systems, users will need to rename to "Opening_Closing"
+
+ Common::FSNode gameDataDir(ConfMan.get("path"));
+ gameDataDir = gameDataDir.getChild("Images");
+
+ if (!gameDataDir.exists())
+ return false;
+
+ Common::FSList fsList;
+ if (!gameDataDir.getChildren(fsList, Common::FSNode::kListDirectoriesOnly, true))
+ return false;
+
+ for (uint i = 0; i < fsList.size() && _introDirectory.empty(); i++) {
+ Common::String name = fsList[i].getName();
+
+ if (name.equalsIgnoreCase("Opening:Closing"))
+ _introDirectory = name;
+ else if (name.equalsIgnoreCase("Opening_Closing"))
+ _introDirectory = name;
+ }
+
+ if (_introDirectory.empty())
+ return false;
+
+ debug(0, "Detected intro location as '%s'", _introDirectory.c_str());
+ _introDirectory = Common::String("Images/") + _introDirectory;
+ return true;
+}
+
+void PegasusEngine::createItems() {
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('N', 'I', 't', 'm'), 0x80);
+
+ uint16 entryCount = res->readUint16BE();
+
+ for (uint16 i = 0; i < entryCount; i++) {
+ ItemID itemID = res->readUint16BE();
+ NeighborhoodID neighborhoodID = res->readUint16BE();
+ RoomID roomID = res->readUint16BE();
+ DirectionConstant direction = res->readByte();
+ res->readByte(); // alignment
+
+ createItem(itemID, neighborhoodID, roomID, direction);
+ }
+
+ delete res;
+}
+
+void PegasusEngine::createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction) {
+ switch (itemID) {
+ case kInterfaceBiochip:
+ // Unused in game, but still in the data and we need to create
+ // it because it's saved/loaded from save files.
+ new BiochipItem(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kAIBiochip:
+ new AIChip(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kPegasusBiochip:
+ new PegasusChip(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kOpticalBiochip:
+ new OpticalChip(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kMapBiochip:
+ new MapChip(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kRetinalScanBiochip:
+ new RetScanChip(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kShieldBiochip:
+ new ShieldChip(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kAirMask:
+ new AirMask(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kKeyCard:
+ new KeyCard(itemID, neighborhoodID, roomID, direction);
+ break;
+ case kGasCanister:
+ new GasCanister(itemID, neighborhoodID, roomID, direction);
+ break;
+ default:
+ // Everything else is a normal inventory item
+ new InventoryItem(itemID, neighborhoodID, roomID, direction);
+ break;
+ }
+}
+
+void PegasusEngine::runIntro() {
+ stopIntroTimer();
+
+ bool skipped = false;
+
+ Video::VideoDecoder *video = new Video::QuickTimeDecoder();
+ if (video->loadFile(_introDirectory + "/BandaiLogo.movie")) {
+ video->start();
+
+ while (!shouldQuit() && !video->endOfVideo() && !skipped) {
+ if (video->needsUpdate()) {
+ const Graphics::Surface *frame = video->decodeNextFrame();
+
+ if (frame) {
+ _system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 0, 0, frame->w, frame->h);
+ _system->updateScreen();
+ }
+ }
+
+ Input input;
+ InputDevice.getInput(input, kFilterAllInput);
+ if (input.anyInput())
+ skipped = true;
+
+ _system->delayMillis(10);
+ }
+ }
+
+ delete video;
+
+ if (shouldQuit() || skipped)
+ return;
+
+ video = new Video::QuickTimeDecoder();
+
+ if (!video->loadFile(_introDirectory + "/Big Movie.movie"))
+ error("Could not load intro movie");
+
+ video->seek(Audio::Timestamp(0, 10 * 600, 600));
+ video->start();
+
+ playMovieScaled(video, 0, 0);
+
+ delete video;
+}
+
+Common::Error PegasusEngine::showLoadDialog() {
+ GUI::SaveLoadChooser slc(_("Load game:"), _("Load"), false);
+
+ Common::String gameId = ConfMan.get("gameid");
+
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+
+ int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+
+ Common::Error result;
+
+ if (slot >= 0) {
+ if (loadGameState(slot).getCode() == Common::kNoError)
+ result = Common::kNoError;
+ else
+ result = Common::kUnknownError;
+ } else {
+ result = Common::kUserCanceled;
+ }
+
+ return result;
+}
+
+Common::Error PegasusEngine::showSaveDialog() {
+ GUI::SaveLoadChooser slc(_("Save game:"), _("Save"), true);
+
+ Common::String gameId = ConfMan.get("gameid");
+
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+
+ int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+
+ Common::Error result;
+
+ if (slot >= 0) {
+ if (saveGameState(slot, slc.getResultString()).getCode() == Common::kNoError)
+ result = Common::kNoError;
+ else
+ result = Common::kUnknownError;
+ } else {
+ result = Common::kUserCanceled;
+ }
+
+ return result;
+}
+
+GUI::Debugger *PegasusEngine::getDebugger() {
+ return _console;
+}
+
+void PegasusEngine::addIdler(Idler *idler) {
+ idler->_nextIdler = _idlerHead;
+ if (_idlerHead)
+ _idlerHead->_prevIdler = idler;
+ idler->_prevIdler = 0;
+ _idlerHead = idler;
+}
+
+void PegasusEngine::removeIdler(Idler *idler) {
+ if (idler->_prevIdler)
+ idler->_prevIdler->_nextIdler = idler->_nextIdler;
+ if (idler->_nextIdler)
+ idler->_nextIdler->_prevIdler = idler->_prevIdler;
+ if (idler == _idlerHead)
+ _idlerHead = idler->_nextIdler;
+ idler->_nextIdler = 0;
+ idler->_prevIdler = 0;
+}
+
+void PegasusEngine::giveIdleTime() {
+ for (Idler *idler = _idlerHead; idler != 0; idler = idler->_nextIdler)
+ idler->useIdleTime();
+}
+
+void PegasusEngine::addTimeBase(TimeBase *timeBase) {
+ _timeBases.push_back(timeBase);
+}
+
+void PegasusEngine::removeTimeBase(TimeBase *timeBase) {
+ _timeBases.remove(timeBase);
+}
+
+bool PegasusEngine::loadFromStream(Common::ReadStream *stream) {
+ // Dispose currently running stuff
+ useMenu(0);
+ useNeighborhood(0);
+ removeAllItemsFromInventory();
+ removeAllItemsFromBiochips();
+ _currentItemID = kNoItemID;
+ _currentBiochipID = kNoItemID;
+
+ if (!g_interface)
+ createInterface();
+
+ // Signature
+ uint32 creator = stream->readUint32BE();
+ if (creator != kPegasusPrimeCreator) {
+ warning("Bad save creator '%s'", tag2str(creator));
+ return false;
+ }
+
+ uint32 gameType = stream->readUint32BE();
+ int saveType;
+
+ switch (gameType) {
+ case kPegasusPrimeDisk1GameType:
+ case kPegasusPrimeDisk2GameType:
+ case kPegasusPrimeDisk3GameType:
+ case kPegasusPrimeDisk4GameType:
+ _currentCD = gameType - kPegasusPrimeDisk1GameType + 1;
+ saveType = kNormalSave;
+ break;
+ case kPegasusPrimeContinueType:
+ saveType = kContinueSave;
+ break;
+ default:
+ // There are five other possible game types on the Pippin
+ // version, but hopefully we don't see any of those here
+ warning("Unhandled pegasus game type '%s'", tag2str(gameType));
+ return false;
+ }
+
+ uint32 version = stream->readUint32BE();
+ if (version != kPegasusPrimeVersion) {
+ warning("Where did you get this save? It's a beta (v%04x)!", version & 0x7fff);
+ return false;
+ }
+
+ // Game State
+ GameState.readGameState(stream);
+
+ // Energy
+ setLastEnergyValue(stream->readUint32BE());
+
+ // Death reason
+ setEnergyDeathReason(stream->readByte());
+
+ // Items
+ _allItems.readFromStream(stream);
+
+ // Inventory
+ byte itemCount = stream->readByte();
+
+ if (itemCount > 0) {
+ for (byte i = 0; i < itemCount; i++) {
+ InventoryItem *inv = (InventoryItem *)_allItems.findItemByID((ItemID)stream->readUint16BE());
+ addItemToInventory(inv);
+ }
+
+ g_interface->setCurrentInventoryItemID((ItemID)stream->readUint16BE());
+ }
+
+ // Biochips
+ byte biochipCount = stream->readByte();
+
+ if (biochipCount > 0) {
+ for (byte i = 0; i < biochipCount; i++) {
+ BiochipItem *biochip = (BiochipItem *)_allItems.findItemByID((ItemID)stream->readUint16BE());
+ addItemToBiochips(biochip);
+ }
+
+ g_interface->setCurrentBiochipID((ItemID)stream->readUint16BE());
+ }
+
+
+ // TODO: Disc check
+
+ // Jump to environment
+ jumpToNewEnvironment(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection());
+ _shellNotification.setNotificationFlags(0, kNeedNewJumpFlag);
+ performJump(GameState.getCurrentNeighborhood());
+
+ // AI rules
+ if (g_AIArea)
+ g_AIArea->readAIRules(stream);
+
+ startNeighborhood();
+
+ // Make a new continue point if this isn't already one
+ if (saveType == kNormalSave)
+ makeContinuePoint();
+
+ return true;
+}
+
+bool PegasusEngine::writeToStream(Common::WriteStream *stream, int saveType) {
+ // WORKAROUND: If we don't have the interface, we can't actually save.
+ // However, we should still have a continue point, so we will just dump that
+ // out. This is needed for saving a game while in the space chase.
+ if (!g_interface) {
+ // Saving a continue stream from a continue stream should
+ // never happen. In addition, we do need to have a continue
+ // stream for this to work.
+ if (saveType != kNormalSave || !_continuePoint)
+ return false;
+
+ writeContinueStream(stream);
+ return true;
+ }
+
+ if (g_neighborhood)
+ g_neighborhood->flushGameState();
+
+ // Signature
+ stream->writeUint32BE(kPegasusPrimeCreator);
+
+ if (saveType == kNormalSave)
+ stream->writeUint32BE(kPegasusPrimeDisk1GameType + _currentCD - 1);
+ else // Continue
+ stream->writeUint32BE(kPegasusPrimeContinueType);
+
+ stream->writeUint32BE(kPegasusPrimeVersion);
+
+ // Game State
+ GameState.writeGameState(stream);
+
+ // Energy
+ stream->writeUint32BE(getSavedEnergyValue());
+
+ // Death reason
+ stream->writeByte(getEnergyDeathReason());
+
+ // Items
+ _allItems.writeToStream(stream);
+
+ // Inventory
+ byte itemCount = _items.getNumItems();
+ stream->writeByte(itemCount);
+
+ if (itemCount > 0) {
+ for (uint32 i = 0; i < itemCount; i++)
+ stream->writeUint16BE(_items.getItemIDAt(i));
+
+ stream->writeUint16BE(g_interface->getCurrentInventoryItem()->getObjectID());
+ }
+
+ // Biochips
+ byte biochipCount = _biochips.getNumItems();
+ stream->writeByte(biochipCount);
+
+ if (biochipCount > 0) {
+ for (uint32 i = 0; i < biochipCount; i++)
+ stream->writeUint16BE(_biochips.getItemIDAt(i));
+
+ stream->writeUint16BE(g_interface->getCurrentBiochip()->getObjectID());
+ }
+
+ // AI rules
+ if (g_AIArea)
+ g_AIArea->writeAIRules(stream);
+
+ return true;
+}
+
+void PegasusEngine::makeContinuePoint() {
+ // WORKAROUND: Do not attempt to make a continue point if the interface is not set
+ // up. The original did *not* do this and attempting to restore the game using the pause
+ // menu during the canyon/space chase sequence would segfault the game and crash the
+ // system. Nice!
+ if (!g_interface)
+ return;
+
+ delete _continuePoint;
+
+ Common::MemoryWriteStreamDynamic newPoint(DisposeAfterUse::NO);
+ writeToStream(&newPoint, kContinueSave);
+ _continuePoint = new Common::MemoryReadStream(newPoint.getData(), newPoint.size(), DisposeAfterUse::YES);
+}
+
+void PegasusEngine::loadFromContinuePoint() {
+ // Failure to load a continue point is fatal
+
+ if (!_continuePoint)
+ error("Attempting to load from non-existant continue point");
+
+ _continuePoint->seek(0);
+
+ if (!loadFromStream(_continuePoint))
+ error("Failed loading continue point");
+}
+
+void PegasusEngine::writeContinueStream(Common::WriteStream *stream) {
+ // We're going to pretty much copy the stream, except for the save type
+ _continuePoint->seek(0);
+ stream->writeUint32BE(_continuePoint->readUint32BE());
+ _continuePoint->readUint32BE(); // skip the continue type
+ stream->writeUint32BE(kPegasusPrimeDisk1GameType + _currentCD - 1);
+
+ // Now just copy over the rest
+ uint32 size = _continuePoint->size() - _continuePoint->pos();
+ byte *data = new byte[size];
+ _continuePoint->read(data, size);
+ stream->write(data, size);
+ delete[] data;
+}
+
+Common::Error PegasusEngine::loadGameState(int slot) {
+ Common::StringArray filenames = _saveFileMan->listSavefiles("pegasus-*.sav");
+ Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filenames[slot]);
+ if (!loadFile)
+ return Common::kUnknownError;
+
+ bool valid = loadFromStream(loadFile);
+ delete loadFile;
+
+ return valid ? Common::kNoError : Common::kUnknownError;
+}
+
+Common::Error PegasusEngine::saveGameState(int slot, const Common::String &desc) {
+ Common::String output = Common::String::format("pegasus-%s.sav", desc.c_str());
+ Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(output, false);
+ if (!saveFile)
+ return Common::kUnknownError;
+
+ bool valid = writeToStream(saveFile, kNormalSave);
+ delete saveFile;
+
+ return valid ? Common::kNoError : Common::kUnknownError;
+}
+
+void PegasusEngine::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ if (&_shellNotification == notification) {
+ switch (flags) {
+ case kGameStartingFlag: {
+ useMenu(new MainMenu());
+
+ if (!isDemo()) {
+ runIntro();
+ resetIntroTimer();
+ } else {
+ showTempScreen("Images/Demo/NGsplashScrn.pict");
+ }
+
+ if (shouldQuit())
+ return;
+
+ _gfx->invalRect(Common::Rect(0, 0, 640, 480));
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ break;
+ }
+ case kPlayerDiedFlag:
+ doDeath();
+ break;
+ case kNeedNewJumpFlag:
+ performJump(GameState.getNextNeighborhood());
+ startNeighborhood();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void PegasusEngine::checkCallBacks() {
+ for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++)
+ (*it)->checkCallBacks();
+}
+
+void PegasusEngine::resetIntroTimer() {
+ if (!isDemo() && _gameMenu && _gameMenu->getObjectID() == kMainMenuID) {
+ _introTimer->stopFuse();
+ _introTimer->primeFuse(kIntroTimeOut);
+ _introTimer->lightFuse();
+ }
+}
+
+void PegasusEngine::introTimerExpired() {
+ if (_gameMenu && _gameMenu->getObjectID() == kMainMenuID) {
+ ((MainMenu *)_gameMenu)->stopMainMenuLoop();
+
+ bool skipped = false;
+
+ Video::VideoDecoder *video = new Video::QuickTimeDecoder();
+ if (!video->loadFile(_introDirectory + "/LilMovie.movie"))
+ error("Failed to load little movie");
+
+ bool saveAllowed = swapSaveAllowed(false);
+ bool openAllowed = swapLoadAllowed(false);
+
+ video->start();
+ skipped = playMovieScaled(video, 0, 0);
+
+ delete video;
+
+ if (shouldQuit())
+ return;
+
+ if (!skipped) {
+ runIntro();
+
+ if (shouldQuit())
+ return;
+ }
+
+ resetIntroTimer();
+ _gfx->invalRect(Common::Rect(0, 0, 640, 480));
+
+ swapSaveAllowed(saveAllowed);
+ swapLoadAllowed(openAllowed);
+
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ }
+}
+
+void PegasusEngine::stopIntroTimer() {
+ if (_introTimer)
+ _introTimer->stopFuse();
+}
+
+void PegasusEngine::delayShell(TimeValue time, TimeScale scale) {
+ if (time == 0 || scale == 0)
+ return;
+
+ uint32 startTime = g_system->getMillis();
+ uint32 timeInMillis = time * 1000 / scale;
+
+ while (g_system->getMillis() < startTime + timeInMillis) {
+ checkCallBacks();
+ _gfx->updateDisplay();
+ }
+}
+
+void PegasusEngine::useMenu(GameMenu *newMenu) {
+ if (_gameMenu) {
+ _gameMenu->restorePreviousHandler();
+ delete _gameMenu;
+ }
+
+ _gameMenu = newMenu;
+
+ if (_gameMenu)
+ _gameMenu->becomeCurrentHandler();
+}
+
+bool PegasusEngine::checkGameMenu() {
+ GameMenuCommand command = kMenuCmdNoCommand;
+
+ if (_gameMenu) {
+ command = _gameMenu->getLastCommand();
+ if (command != kMenuCmdNoCommand) {
+ _gameMenu->clearLastCommand();
+ doGameMenuCommand(command);
+ }
+ }
+
+ return command != kMenuCmdNoCommand;
+}
+
+void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) {
+ Common::Error result;
+
+ switch (command) {
+ case kMenuCmdStartAdventure:
+ stopIntroTimer();
+ GameState.setWalkthroughMode(false);
+ startNewGame();
+ break;
+ case kMenuCmdCredits:
+ if (isDemo()) {
+ showTempScreen("Images/Demo/DemoCredits.pict");
+ _gfx->doFadeOutSync();
+ _gfx->updateDisplay();
+ _gfx->doFadeInSync();
+ } else {
+ stopIntroTimer();
+ _gfx->doFadeOutSync();
+ useMenu(new CreditsMenu());
+ _gfx->updateDisplay();
+ _gfx->doFadeInSync();
+ }
+ break;
+ case kMenuCmdQuit:
+ case kMenuCmdDeathQuitDemo:
+ if (isDemo())
+ showTempScreen("Images/Demo/NGquitScrn.pict");
+ _system->quit();
+ break;
+ case kMenuCmdOverview:
+ stopIntroTimer();
+ doInterfaceOverview();
+ resetIntroTimer();
+ break;
+ case kMenuCmdStartWalkthrough:
+ stopIntroTimer();
+ GameState.setWalkthroughMode(true);
+ startNewGame();
+ break;
+ case kMenuCmdRestore:
+ stopIntroTimer();
+ // fall through
+ case kMenuCmdDeathRestore:
+ result = showLoadDialog();
+ if (command == kMenuCmdRestore && result.getCode() != Common::kNoError)
+ resetIntroTimer();
+ break;
+ case kMenuCmdCreditsMainMenu:
+ _gfx->doFadeOutSync();
+ useMenu(new MainMenu());
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ _gfx->doFadeInSync();
+ resetIntroTimer();
+ break;
+ case kMenuCmdDeathContinue:
+ if (((DeathMenu *)_gameMenu)->playerWon()) {
+ if (isDemo()) {
+ showTempScreen("Images/Demo/DemoCredits.pict");
+ _gfx->doFadeOutSync();
+ _gfx->updateDisplay();
+ _gfx->doFadeInSync();
+ } else {
+ _gfx->doFadeOutSync();
+ useMenu(0);
+ _gfx->clearScreen();
+ _gfx->updateDisplay();
+
+ Video::VideoDecoder *video = new Video::QuickTimeDecoder();
+ if (!video->loadFile(_introDirectory + "/Closing.movie"))
+ error("Could not load closing movie");
+
+ uint16 x = (640 - video->getWidth() * 2) / 2;
+ uint16 y = (480 - video->getHeight() * 2) / 2;
+
+ video->start();
+ playMovieScaled(video, x, y);
+
+ delete video;
+
+ if (shouldQuit())
+ return;
+
+ useMenu(new MainMenu());
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ _gfx->doFadeInSync();
+ resetIntroTimer();
+ }
+ } else {
+ loadFromContinuePoint();
+ }
+ break;
+ case kMenuCmdDeathMainMenuDemo:
+ case kMenuCmdDeathMainMenu:
+ _gfx->doFadeOutSync();
+ useMenu(new MainMenu());
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ _gfx->doFadeInSync();
+ resetIntroTimer();
+ break;
+ case kMenuCmdPauseSave:
+ if (showSaveDialog().getCode() != Common::kUserCanceled)
+ pauseMenu(false);
+ break;
+ case kMenuCmdPauseContinue:
+ pauseMenu(false);
+ break;
+ case kMenuCmdPauseRestore:
+ makeContinuePoint();
+ result = showLoadDialog();
+
+ if (result.getCode() == Common::kNoError) {
+ // Successfully loaded, unpause the game
+ pauseMenu(false);
+ } else if (result.getCode() != Common::kUserCanceled) {
+ // Try to get us back to a sane state
+ loadFromContinuePoint();
+ }
+ break;
+ case kMenuCmdPauseQuit:
+ _gfx->doFadeOutSync();
+ throwAwayEverything();
+ pauseMenu(false);
+ useMenu(new MainMenu());
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ _gfx->doFadeInSync();
+ resetIntroTimer();
+ break;
+ case kMenuCmdNoCommand:
+ break;
+ default:
+ error("Unknown menu command %d", command);
+ }
+}
+
+void PegasusEngine::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ if (!checkGameMenu())
+ shellGameInput(input, cursorSpot);
+
+ // Handle the console here
+ if (input.isConsoleRequested()) {
+ _console->attach();
+ _console->onFrame();
+ }
+
+ // Handle save requests here
+ if (_saveRequested && _saveAllowed) {
+ _saveRequested = false;
+
+ // Can only save during a game and not in the demo
+ if (g_neighborhood && !isDemo()) {
+ pauseEngine(true);
+ showSaveDialog();
+ pauseEngine(false);
+ }
+ }
+
+ // Handle load requests here
+ if (_loadRequested && _loadAllowed) {
+ _loadRequested = false;
+
+ // WORKAROUND: Do not entertain load requests when the pause menu is up
+ // The original did and the game entered a bad state after loading.
+ // It's theoretically possible to make it so it does work while the
+ // pause menu is up, but the pause state of the engine is just too weird.
+ // Just use the pause menu's restore button since it's there for that
+ // for you to load anyway.
+ if (!isDemo() && !(_gameMenu && _gameMenu->getObjectID() == kPauseMenuID)) {
+ pauseEngine(true);
+
+ if (g_neighborhood) {
+ makeContinuePoint();
+
+ Common::Error result = showLoadDialog();
+ if (result.getCode() != Common::kNoError && result.getCode() != Common::kUserCanceled)
+ loadFromContinuePoint();
+ } else {
+ if (_introTimer)
+ _introTimer->stopFuse();
+
+ Common::Error result = showLoadDialog();
+ if (result.getCode() != Common::kNoError) {
+ if (!_gameMenu) {
+ useMenu(new MainMenu());
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ }
+
+ resetIntroTimer();
+ }
+ }
+
+ pauseEngine(false);
+ }
+ }
+}
+
+void PegasusEngine::doInterfaceOverview() {
+ static const short kNumOverviewSpots = 11;
+ static const Common::Rect overviewSpots[kNumOverviewSpots] = {
+ Common::Rect(354, 318, 354 + 204, 318 + 12),
+ Common::Rect(211, 34, 211 + 114, 34 + 28),
+ Common::Rect(502, 344, 502 + 138, 344 + 120),
+ Common::Rect(132, 40, 132 + 79, 40 + 22),
+ Common::Rect(325, 40, 332 + 115, 40 + 22),
+ Common::Rect(70, 318, 70 + 284, 318 + 12),
+ Common::Rect(76, 334, 76 + 96, 334 + 96),
+ Common::Rect(64, 64, 64 + 512, 64 + 256),
+ Common::Rect(364, 334, 364 + 96, 334 + 96),
+ Common::Rect(172, 334, 172 + 192, 334 + 96),
+ Common::Rect(542, 36, 542 + 58, 36 + 20)
+ };
+
+ _gfx->doFadeOutSync();
+ useMenu(0);
+
+ Picture leftBackground(kNoDisplayElement);
+ leftBackground.initFromPICTFile("Images/Interface/OVLeft.mac");
+ leftBackground.setDisplayOrder(0);
+ leftBackground.moveElementTo(kBackground1Left, kBackground1Top);
+ leftBackground.startDisplaying();
+ leftBackground.show();
+
+ Picture topBackground(kNoDisplayElement);
+ topBackground.initFromPICTFile("Images/Interface/OVTop.mac");
+ topBackground.setDisplayOrder(0);
+ topBackground.moveElementTo(kBackground2Left, kBackground2Top);
+ topBackground.startDisplaying();
+ topBackground.show();
+
+ Picture rightBackground(kNoDisplayElement);
+ rightBackground.initFromPICTFile("Images/Interface/OVRight.mac");
+ rightBackground.setDisplayOrder(0);
+ rightBackground.moveElementTo(kBackground3Left, kBackground3Top);
+ rightBackground.startDisplaying();
+ rightBackground.show();
+
+ Picture bottomBackground(kNoDisplayElement);
+ bottomBackground.initFromPICTFile("Images/Interface/OVBottom.mac");
+ bottomBackground.setDisplayOrder(0);
+ bottomBackground.moveElementTo(kBackground4Left, kBackground4Top);
+ bottomBackground.startDisplaying();
+ bottomBackground.show();
+
+ Picture controllerHighlight(kNoDisplayElement);
+ controllerHighlight.initFromPICTFile("Images/Interface/OVcontrollerHilite.mac");
+ controllerHighlight.setDisplayOrder(0);
+ controllerHighlight.moveElementTo(kOverviewControllerLeft, kOverviewControllerTop);
+ controllerHighlight.startDisplaying();
+
+ Movie overviewText(kNoDisplayElement);
+ overviewText.initFromMovieFile("Images/Interface/Overview Mac.movie");
+ overviewText.setDisplayOrder(0);
+ overviewText.moveElementTo(kNavAreaLeft, kNavAreaTop);
+ overviewText.startDisplaying();
+ overviewText.show();
+ overviewText.redrawMovieWorld();
+
+ DropHighlight highlight(kNoDisplayElement);
+ highlight.setDisplayOrder(1);
+ highlight.startDisplaying();
+ highlight.setHighlightThickness(4);
+ highlight.setHighlightColor(g_system->getScreenFormat().RGBToColor(239, 239, 0));
+ highlight.setHighlightCornerDiameter(8);
+
+ Input input;
+ InputDevice.getInput(input, kFilterAllInput);
+
+ Common::Point cursorLoc;
+ input.getInputLocation(cursorLoc);
+
+ uint16 i;
+ for (i = 0; i < kNumOverviewSpots; ++i)
+ if (overviewSpots[i].contains(cursorLoc))
+ break;
+
+ TimeValue time;
+ if (i == kNumOverviewSpots)
+ time = 5;
+ else if (i > 4)
+ time = i + 1;
+ else
+ time = i;
+
+ if (time == 2) {
+ highlight.hide();
+ controllerHighlight.show();
+ } else if (i != kNumOverviewSpots) {
+ controllerHighlight.hide();
+ Common::Rect r = overviewSpots[i];
+ r.grow(5);
+ highlight.setBounds(r);
+ highlight.show();
+ } else {
+ highlight.hide();
+ controllerHighlight.hide();
+ }
+
+ overviewText.setTime(time * 3 + 2, 15);
+ overviewText.redrawMovieWorld();
+
+ _cursor->setCurrentFrameIndex(3);
+ _cursor->show();
+
+ _gfx->updateDisplay();
+ _gfx->doFadeInSync();
+
+ for (;;) {
+ InputDevice.getInput(input, kFilterAllInput);
+
+ if (input.anyInput() || shouldQuit() || _loadRequested || _saveRequested)
+ break;
+
+ input.getInputLocation(cursorLoc);
+ for (i = 0; i < kNumOverviewSpots; ++i)
+ if (overviewSpots[i].contains(cursorLoc))
+ break;
+
+ if (i == kNumOverviewSpots)
+ time = 5;
+ else if (i > 4)
+ time = i + 1;
+ else
+ time = i;
+
+ if (time == 2) {
+ highlight.hide();
+ controllerHighlight.show();
+ } else if (i != kNumOverviewSpots) {
+ controllerHighlight.hide();
+ Common::Rect r = overviewSpots[i];
+ r.grow(5);
+ highlight.setBounds(r);
+ highlight.show();
+ } else {
+ highlight.hide();
+ controllerHighlight.hide();
+ }
+
+ overviewText.setTime(time * 3 + 2, 15);
+ overviewText.redrawMovieWorld();
+
+ refreshDisplay();
+ }
+
+ if (shouldQuit())
+ return;
+
+ highlight.hide();
+ _cursor->hide();
+
+ _gfx->doFadeOutSync();
+ useMenu(new MainMenu());
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ _gfx->doFadeInSync();
+
+ _saveRequested = false;
+ _loadRequested = false;
+}
+
+void PegasusEngine::showTempScreen(const Common::String &fileName) {
+ _gfx->doFadeOutSync();
+
+ Picture picture(0);
+ picture.initFromPICTFile(fileName);
+ picture.setDisplayOrder(kMaxAvailableOrder);
+ picture.startDisplaying();
+ picture.show();
+ _gfx->updateDisplay();
+
+ _gfx->doFadeInSync();
+
+ // Wait for the next event
+ bool done = false;
+ while (!shouldQuit() && !done) {
+ Common::Event event;
+ while (_eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ case Common::EVENT_KEYDOWN:
+ done = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ _system->delayMillis(10);
+ }
+}
+
+void PegasusEngine::refreshDisplay() {
+ giveIdleTime();
+ _gfx->updateDisplay();
+}
+
+void PegasusEngine::resetEnergyDeathReason() {
+ switch (getCurrentNeighborhoodID()) {
+ case kMarsID:
+ _deathReason = kDeathArrestedInMars;
+ break;
+ case kNoradAlphaID:
+ case kNoradDeltaID:
+ _deathReason = kDeathArrestedInNorad;
+ break;
+ case kWSCID:
+ _deathReason = kDeathArrestedInWSC;
+ break;
+ default:
+ _deathReason = kDeathStranded;
+ break;
+ }
+}
+
+bool PegasusEngine::playerHasItem(const Item *item) {
+ return playerHasItemID(item->getObjectID());
+}
+
+bool PegasusEngine::playerHasItemID(const ItemID itemID) {
+ return itemInInventory(itemID) || itemInBiochips(itemID);
+}
+
+InventoryItem *PegasusEngine::getCurrentInventoryItem() {
+ if (g_interface)
+ return g_interface->getCurrentInventoryItem();
+
+ return 0;
+}
+
+bool PegasusEngine::itemInInventory(InventoryItem *item) {
+ return _items.itemInInventory(item);
+}
+
+bool PegasusEngine::itemInInventory(ItemID id) {
+ return _items.itemInInventory(id);
+}
+
+BiochipItem *PegasusEngine::getCurrentBiochip() {
+ if (g_interface)
+ return g_interface->getCurrentBiochip();
+
+ return 0;
+}
+
+bool PegasusEngine::itemInBiochips(BiochipItem *item) {
+ return _biochips.itemInInventory(item);
+}
+
+bool PegasusEngine::itemInBiochips(ItemID id) {
+ return _biochips.itemInInventory(id);
+}
+
+bool PegasusEngine::playerAlive() {
+ return (_shellNotification.getNotificationFlags() & kPlayerDiedFlag) == 0;
+}
+
+Common::String PegasusEngine::getBriefingMovie() {
+ if (_neighborhood)
+ return _neighborhood->getBriefingMovie();
+
+ return Common::String();
+}
+
+Common::String PegasusEngine::getEnvScanMovie() {
+ if (_neighborhood)
+ return _neighborhood->getEnvScanMovie();
+
+ return Common::String();
+}
+
+uint PegasusEngine::getNumHints() {
+ if (_neighborhood)
+ return _neighborhood->getNumHints();
+
+ return 0;
+}
+
+Common::String PegasusEngine::getHintMovie(uint hintNum) {
+ if (_neighborhood)
+ return _neighborhood->getHintMovie(hintNum);
+
+ return Common::String();
+}
+
+bool PegasusEngine::canSolve() {
+ if (_neighborhood)
+ return _neighborhood->canSolve();
+
+ return false;
+}
+
+void PegasusEngine::prepareForAIHint(const Common::String &movieName) {
+ if (g_neighborhood)
+ g_neighborhood->prepareForAIHint(movieName);
+}
+
+void PegasusEngine::cleanUpAfterAIHint(const Common::String &movieName) {
+ if (g_neighborhood)
+ g_neighborhood->cleanUpAfterAIHint(movieName);
+}
+
+void PegasusEngine::jumpToNewEnvironment(const NeighborhoodID neighborhoodID, const RoomID roomID, const DirectionConstant direction) {
+ GameState.setNextLocation(neighborhoodID, roomID, direction);
+ _shellNotification.setNotificationFlags(kNeedNewJumpFlag, kNeedNewJumpFlag);
+}
+
+void PegasusEngine::checkFlashlight() {
+ if (_neighborhood)
+ _neighborhood->checkFlashlight();
+}
+
+bool PegasusEngine::playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16 y) {
+ bool skipped = false;
+
+ while (!shouldQuit() && !video->endOfVideo() && !skipped) {
+ if (video->needsUpdate()) {
+ const Graphics::Surface *frame = video->decodeNextFrame();
+
+ if (frame)
+ drawScaledFrame(frame, x, y);
+ }
+
+ Input input;
+ InputDevice.getInput(input, kFilterAllInput);
+ if (input.anyInput() || _saveRequested || _loadRequested)
+ skipped = true;
+
+ _system->delayMillis(10);
+ }
+
+ return skipped;
+}
+
+void PegasusEngine::die(const DeathReason reason) {
+ Input dummy;
+ if (isDragging())
+ _itemDragger.stopTracking(dummy);
+
+ _deathReason = reason;
+ _shellNotification.setNotificationFlags(kPlayerDiedFlag, kPlayerDiedFlag);
+}
+
+void PegasusEngine::doDeath() {
+ _gfx->doFadeOutSync();
+ throwAwayEverything();
+ useMenu(new DeathMenu(_deathReason));
+ _gfx->updateDisplay();
+ _gfx->doFadeInSync();
+}
+
+void PegasusEngine::throwAwayEverything() {
+ if (_items.getNumItems() != 0 && g_interface)
+ _currentItemID = g_interface->getCurrentInventoryItem()->getObjectID();
+ else
+ _currentItemID = kNoItemID;
+
+ if (_biochips.getNumItems() != 0 && g_interface)
+ _currentBiochipID = g_interface->getCurrentBiochip()->getObjectID();
+ else
+ _currentBiochipID = kNoItemID;
+
+ useMenu(0);
+ useNeighborhood(0);
+
+ delete g_interface;
+ g_interface = 0;
+}
+
+void PegasusEngine::processShell() {
+ checkCallBacks();
+ checkNotifications();
+ InputHandler::pollForInput();
+ refreshDisplay();
+}
+
+void PegasusEngine::createInterface() {
+ if (!g_interface)
+ new Interface();
+
+ g_interface->createInterface();
+}
+
+void PegasusEngine::setGameMode(const GameMode newMode) {
+ if (newMode != _gameMode && canSwitchGameMode(newMode, _gameMode)) {
+ switchGameMode(newMode, _gameMode);
+ _gameMode = newMode;
+ }
+}
+
+void PegasusEngine::switchGameMode(const GameMode newMode, const GameMode oldMode) {
+ // Start raising panels before lowering panels, to give the activating panel time
+ // to set itself up without cutting into the lowering panel's animation time.
+
+ if (_switchModesSync) {
+ if (newMode == kModeInventoryPick)
+ raiseInventoryDrawerSync();
+ else if (newMode == kModeBiochipPick)
+ raiseBiochipDrawerSync();
+ else if (newMode == kModeInfoScreen)
+ showInfoScreen();
+
+ if (oldMode == kModeInventoryPick)
+ lowerInventoryDrawerSync();
+ else if (oldMode == kModeBiochipPick)
+ lowerBiochipDrawerSync();
+ else if (oldMode == kModeInfoScreen)
+ hideInfoScreen();
+ } else {
+ if (newMode == kModeInventoryPick)
+ raiseInventoryDrawer();
+ else if (newMode == kModeBiochipPick)
+ raiseBiochipDrawer();
+ else if (newMode == kModeInfoScreen)
+ showInfoScreen();
+
+ if (oldMode == kModeInventoryPick)
+ lowerInventoryDrawer();
+ else if (oldMode == kModeBiochipPick)
+ lowerBiochipDrawer();
+ else if (oldMode == kModeInfoScreen)
+ hideInfoScreen();
+ }
+}
+
+bool PegasusEngine::canSwitchGameMode(const GameMode newMode, const GameMode oldMode) {
+ if (newMode == kModeInventoryPick && oldMode == kModeBiochipPick)
+ return false;
+ if (newMode == kModeBiochipPick && oldMode == kModeInventoryPick)
+ return false;
+ return true;
+}
+
+bool PegasusEngine::itemInLocation(const ItemID itemID, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) {
+ NeighborhoodID itemNeighborhood;
+ RoomID itemRoom;
+ DirectionConstant itemDirection;
+
+ Item *item = _allItems.findItemByID(itemID);
+ item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
+
+ return itemNeighborhood == neighborhood && itemRoom == room && itemDirection == direction;
+}
+
+InventoryResult PegasusEngine::addItemToInventory(InventoryItem *item) {
+ InventoryResult result;
+
+ do {
+ if (g_interface)
+ result = g_interface->addInventoryItem(item);
+ else
+ result = _items.addItem(item);
+
+ if (result == kTooMuchWeight)
+ destroyInventoryItem(pickItemToDestroy());
+ } while (result != kInventoryOK);
+
+ GameState.setTakenItem(item, true);
+ if (g_neighborhood)
+ g_neighborhood->pickedUpItem(item);
+
+ g_AIArea->checkMiddleArea();
+
+ return result;
+}
+
+void PegasusEngine::useNeighborhood(Neighborhood *neighborhood) {
+ delete _neighborhood;
+ _neighborhood = neighborhood;
+
+ if (_neighborhood) {
+ InputHandler::setInputHandler(_neighborhood);
+ _neighborhood->init();
+ _neighborhood->moveNavTo(kNavAreaLeft, kNavAreaTop);
+ g_interface->setDate(_neighborhood->getDateResID());
+ } else {
+ InputHandler::setInputHandler(this);
+ }
+}
+
+void PegasusEngine::performJump(NeighborhoodID neighborhoodID) {
+ if (_neighborhood)
+ useNeighborhood(0);
+
+ // Sub chase is special
+ if (neighborhoodID == kNoradSubChaseID) {
+ throwAwayEverything();
+ _loadAllowed = false;
+ doSubChase();
+
+ if (shouldQuit())
+ return;
+
+ neighborhoodID = kNoradDeltaID;
+ GameState.setNextRoom(kNorad41);
+ GameState.setNextDirection(kEast);
+ _loadAllowed = true;
+ }
+
+ Neighborhood *neighborhood;
+ makeNeighborhood(neighborhoodID, neighborhood);
+ useNeighborhood(neighborhood);
+
+ // Update the CD variable (used just for saves currently)
+ _currentCD = getNeighborhoodCD(neighborhoodID);
+}
+
+void PegasusEngine::startNeighborhood() {
+ if (g_interface && _currentItemID != kNoItemID)
+ g_interface->setCurrentInventoryItemID(_currentItemID);
+
+ if (g_interface && _currentBiochipID != kNoItemID)
+ g_interface->setCurrentBiochipID(_currentBiochipID);
+
+ setGameMode(kModeNavigation);
+
+ if (_neighborhood)
+ _neighborhood->start();
+}
+
+void PegasusEngine::startNewGame() {
+ // WORKAROUND: The original game ignored the menu difficulty
+ // setting. We're going to pass it through here so that
+ // the menu actually makes sense now.
+ bool isWalkthrough = GameState.getWalkthroughMode();
+ GameState.resetGameState();
+ GameState.setWalkthroughMode(isWalkthrough);
+
+ // TODO: Enable erase
+ _gfx->doFadeOutSync();
+ useMenu(0);
+ _gfx->updateDisplay();
+ _gfx->enableUpdates();
+
+ createInterface();
+
+ if (isDemo()) {
+ setLastEnergyValue(kFullEnergy);
+ jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth);
+ GameState.setPrehistoricSeenTimeStream(false);
+ GameState.setPrehistoricSeenFlyer1(false);
+ GameState.setPrehistoricSeenFlyer2(false);
+ GameState.setPrehistoricSeenBridgeZoom(false);
+ GameState.setPrehistoricBreakerThrown(false);
+ } else {
+ jumpToNewEnvironment(kCaldoriaID, kCaldoria00, kEast);
+ }
+
+ removeAllItemsFromInventory();
+ removeAllItemsFromBiochips();
+
+ BiochipItem *biochip = (BiochipItem *)_allItems.findItemByID(kAIBiochip);
+ addItemToBiochips(biochip);
+
+ if (isDemo()) {
+ biochip = (BiochipItem *)_allItems.findItemByID(kPegasusBiochip);
+ addItemToBiochips(biochip);
+ biochip = (BiochipItem *)_allItems.findItemByID(kMapBiochip);
+ addItemToBiochips(biochip);
+ InventoryItem *item = (InventoryItem *)_allItems.findItemByID(kKeyCard);
+ addItemToInventory(item);
+ item = (InventoryItem *)_allItems.findItemByID(kJourneymanKey);
+ addItemToInventory(item);
+ _currentItemID = kJourneymanKey;
+ } else {
+ _currentItemID = kNoItemID;
+ }
+
+ _currentBiochipID = kAIBiochip;
+
+ // Clear jump notification flags and just perform the jump...
+ _shellNotification.setNotificationFlags(0, kNeedNewJumpFlag);
+
+ performJump(GameState.getNextNeighborhood());
+
+ startNeighborhood();
+}
+
+void PegasusEngine::makeNeighborhood(NeighborhoodID neighborhoodID, Neighborhood *&neighborhood) {
+ // TODO: CD check
+
+ switch (neighborhoodID) {
+ case kCaldoriaID:
+ neighborhood = new Caldoria(g_AIArea, this);
+ break;
+ case kMarsID:
+ neighborhood = new Mars(g_AIArea, this);
+ break;
+ case kPrehistoricID:
+ neighborhood = new Prehistoric(g_AIArea, this);
+ break;
+ case kFullTSAID:
+ neighborhood = new FullTSA(g_AIArea, this);
+ break;
+ case kTinyTSAID:
+ neighborhood = new TinyTSA(g_AIArea, this);
+ break;
+ case kWSCID:
+ neighborhood = new WSC(g_AIArea, this);
+ break;
+ case kNoradAlphaID:
+ neighborhood = new NoradAlpha(g_AIArea, this);
+ break;
+ case kNoradDeltaID:
+ createInterface();
+ neighborhood = new NoradDelta(g_AIArea, this);
+ break;
+ default:
+ error("Unknown neighborhood %d", neighborhoodID);
+ }
+}
+
+bool PegasusEngine::wantsCursor() {
+ return _gameMenu == 0;
+}
+
+void PegasusEngine::updateCursor(const Common::Point, const Hotspot *cursorSpot) {
+ if (_itemDragger.isTracking()) {
+ _cursor->setCurrentFrameIndex(5);
+ } else {
+ if (!cursorSpot) {
+ _cursor->setCurrentFrameIndex(0);
+ } else {
+ uint32 id = cursorSpot->getObjectID();
+
+ switch (id) {
+ case kCurrentItemSpotID:
+ if (countInventoryItems() != 0)
+ _cursor->setCurrentFrameIndex(4);
+ else
+ _cursor->setCurrentFrameIndex(0);
+ break;
+ default:
+ HotSpotFlags flags = cursorSpot->getHotspotFlags();
+
+ if (flags & kZoomInSpotFlag)
+ _cursor->setCurrentFrameIndex(1);
+ else if (flags & kZoomOutSpotFlag)
+ _cursor->setCurrentFrameIndex(2);
+ else if (flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag))
+ _cursor->setCurrentFrameIndex(4);
+ else if (flags & kJMPClickingSpotFlags)
+ _cursor->setCurrentFrameIndex(3);
+ else
+ _cursor->setCurrentFrameIndex(0);
+ }
+ }
+ }
+}
+
+void PegasusEngine::toggleInventoryDisplay() {
+ if (_gameMode == kModeInventoryPick)
+ setGameMode(kModeNavigation);
+ else
+ setGameMode(kModeInventoryPick);
+}
+
+void PegasusEngine::toggleBiochipDisplay() {
+ if (_gameMode == kModeBiochipPick)
+ setGameMode(kModeNavigation);
+ else
+ setGameMode(kModeBiochipPick);
+}
+
+void PegasusEngine::showInfoScreen() {
+ if (g_neighborhood) {
+ // Break the input handler chain...
+ _savedHandler = InputHandler::getCurrentHandler();
+ InputHandler::setInputHandler(this);
+
+ Picture *pushPicture = ((Neighborhood *)g_neighborhood)->getTurnPushPicture();
+
+ _bigInfoMovie.shareSurface(pushPicture);
+ _smallInfoMovie.shareSurface(pushPicture);
+
+ g_neighborhood->hideNav();
+
+ _smallInfoMovie.initFromMovieFile("Images/Items/Info Right Movie");
+ _smallInfoMovie.setDisplayOrder(kInfoSpinOrder);
+ _smallInfoMovie.moveElementTo(kNavAreaLeft + 304, kNavAreaTop + 8);
+ _smallInfoMovie.moveMovieBoxTo(304, 8);
+ _smallInfoMovie.startDisplaying();
+ _smallInfoMovie.show();
+
+ TimeValue startTime, stopTime;
+ g_AIArea->getSmallInfoSegment(startTime, stopTime);
+ _smallInfoMovie.setSegment(startTime, stopTime);
+ _smallInfoMovie.setTime(startTime);
+ _smallInfoMovie.setFlags(kLoopTimeBase);
+
+ _bigInfoMovie.initFromMovieFile("Images/Items/Info Left Movie");
+ _bigInfoMovie.setDisplayOrder(kInfoBackgroundOrder);
+ _bigInfoMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+ _bigInfoMovie.startDisplaying();
+ _bigInfoMovie.show();
+ _bigInfoMovie.setTime(g_AIArea->getBigInfoTime());
+
+ _bigInfoMovie.redrawMovieWorld();
+ _smallInfoMovie.redrawMovieWorld();
+ _smallInfoMovie.start();
+ }
+}
+
+void PegasusEngine::hideInfoScreen() {
+ if (g_neighborhood) {
+ InputHandler::setInputHandler(_savedHandler);
+
+ _bigInfoMovie.hide();
+ _bigInfoMovie.stopDisplaying();
+ _bigInfoMovie.releaseMovie();
+
+ _smallInfoMovie.hide();
+ _smallInfoMovie.stopDisplaying();
+ _smallInfoMovie.stop();
+ _smallInfoMovie.releaseMovie();
+
+ g_neighborhood->showNav();
+ }
+}
+
+void PegasusEngine::raiseInventoryDrawer() {
+ if (g_interface)
+ g_interface->raiseInventoryDrawer();
+}
+
+void PegasusEngine::raiseBiochipDrawer() {
+ if (g_interface)
+ g_interface->raiseBiochipDrawer();
+}
+
+void PegasusEngine::lowerInventoryDrawer() {
+ if (g_interface)
+ g_interface->lowerInventoryDrawer();
+}
+
+void PegasusEngine::lowerBiochipDrawer() {
+ if (g_interface)
+ g_interface->lowerBiochipDrawer();
+}
+
+void PegasusEngine::raiseInventoryDrawerSync() {
+ if (g_interface)
+ g_interface->raiseInventoryDrawerSync();
+}
+
+void PegasusEngine::raiseBiochipDrawerSync() {
+ if (g_interface)
+ g_interface->raiseBiochipDrawerSync();
+}
+
+void PegasusEngine::lowerInventoryDrawerSync() {
+ if (g_interface)
+ g_interface->lowerInventoryDrawerSync();
+}
+
+void PegasusEngine::lowerBiochipDrawerSync() {
+ if (g_interface)
+ g_interface->lowerBiochipDrawerSync();
+}
+
+void PegasusEngine::toggleInfo() {
+ if (_gameMode == kModeInfoScreen)
+ setGameMode(kModeNavigation);
+ else if (_gameMode == kModeNavigation)
+ setGameMode(kModeInfoScreen);
+}
+
+void PegasusEngine::dragTerminated(const Input &) {
+ Hotspot *finalSpot = _itemDragger.getLastHotspot();
+ InventoryResult result;
+
+ if (_dragType == kDragInventoryPickup) {
+ if (finalSpot && finalSpot->getObjectID() == kInventoryDropSpotID)
+ result = addItemToInventory((InventoryItem *)_draggingItem);
+ else
+ result = kTooMuchWeight;
+
+ if (result != kInventoryOK)
+ autoDragItemIntoRoom(_draggingItem, _draggingSprite);
+ else
+ delete _draggingSprite;
+ } else if (_dragType == kDragBiochipPickup) {
+ if (finalSpot && finalSpot->getObjectID() == kBiochipDropSpotID)
+ result = addItemToBiochips((BiochipItem *)_draggingItem);
+ else
+ result = kTooMuchWeight;
+
+ if (result != kInventoryOK)
+ autoDragItemIntoRoom(_draggingItem, _draggingSprite);
+ else
+ delete _draggingSprite;
+ } else if (_dragType == kDragInventoryUse) {
+ if (finalSpot && (finalSpot->getHotspotFlags() & kDropItemSpotFlag) != 0) {
+ // *** Need to decide on a case by case basis what to do here.
+ // the crowbar should break the cover off the Mars reactor if its frozen, the
+ // global transport card should slide through the slot, the oxygen mask should
+ // attach to the filling station, and so on...
+ _neighborhood->dropItemIntoRoom(_draggingItem, finalSpot);
+ delete _draggingSprite;
+ } else {
+ autoDragItemIntoInventory(_draggingItem, _draggingSprite);
+ }
+ }
+
+ _dragType = kDragNoDrag;
+
+ if (g_AIArea)
+ g_AIArea->unlockAI();
+}
+
+
+void PegasusEngine::dragItem(const Input &input, Item *item, DragType type) {
+ _draggingItem = item;
+ _dragType = type;
+
+ // Create the sprite.
+ _draggingSprite = _draggingItem->getDragSprite(kDraggingSpriteID);
+ Common::Point where;
+ input.getInputLocation(where);
+ Common::Rect r1;
+ _draggingSprite->getBounds(r1);
+ r1 = Common::Rect::center(where.x, where.y, r1.width(), r1.height());
+ _draggingSprite->setBounds(r1);
+
+ // Set up drag constraints.
+ DisplayElement *navMovie = _gfx->findDisplayElement(kNavMovieID);
+ Common::Rect r2;
+ navMovie->getBounds(r2);
+ r2.left -= r1.width() / 3;
+ r2.right += r1.width() / 3;
+ r2.top -= r1.height() / 3;
+ r2.bottom += r2.height() / 3;
+
+ r1 = Common::Rect(-30000, -30000, 30000, 30000);
+ _itemDragger.setDragConstraints(r2, r1);
+
+ // Start dragging.
+ _draggingSprite->setDisplayOrder(kDragSpriteOrder);
+ _draggingSprite->startDisplaying();
+ _draggingSprite->show();
+ _itemDragger.setDragSprite(_draggingSprite);
+ _itemDragger.setNextHandler(_neighborhood);
+ _itemDragger.startTracking(input);
+
+ if (g_AIArea)
+ g_AIArea->lockAIOut();
+}
+
+void PegasusEngine::shellGameInput(const Input &input, const Hotspot *cursorSpot) {
+ if (_gameMode == kModeInfoScreen) {
+ if (JMPPPInput::isToggleAIMiddleInput(input)) {
+ LowerClientSignature middleOwner = g_AIArea->getMiddleAreaOwner();
+ g_AIArea->toggleMiddleAreaOwner();
+
+ if (middleOwner != g_AIArea->getMiddleAreaOwner()) {
+ _bigInfoMovie.setTime(g_AIArea->getBigInfoTime());
+ _smallInfoMovie.stop();
+ _smallInfoMovie.setFlags(0);
+
+ TimeValue startTime, stopTime;
+ g_AIArea->getSmallInfoSegment(startTime, stopTime);
+ _smallInfoMovie.setSegment(startTime, stopTime);
+ _smallInfoMovie.setTime(startTime);
+ _smallInfoMovie.setFlags(kLoopTimeBase);
+
+ _bigInfoMovie.redrawMovieWorld();
+ _smallInfoMovie.redrawMovieWorld();
+ _smallInfoMovie.start();
+ }
+ }
+ } else {
+ if (JMPPPInput::isRaiseInventoryInput(input))
+ toggleInventoryDisplay();
+
+ if (JMPPPInput::isRaiseBiochipsInput(input))
+ toggleBiochipDisplay();
+
+ if (JMPPPInput::isTogglePauseInput(input) && _neighborhood)
+ pauseMenu(!isPaused());
+ }
+
+ if (JMPPPInput::isToggleInfoInput(input))
+ toggleInfo();
+}
+
+void PegasusEngine::activateHotspots() {
+ if (_gameMode == kModeInfoScreen) {
+ _allHotspots.activateOneHotspot(kInfoReturnSpotID);
+ } else {
+ // Set up hot spots.
+ if (_dragType == kDragInventoryPickup)
+ _allHotspots.activateOneHotspot(kInventoryDropSpotID);
+ else if (_dragType == kDragBiochipPickup)
+ _allHotspots.activateOneHotspot(kBiochipDropSpotID);
+ else if (_dragType == kDragNoDrag)
+ _allHotspots.activateMaskedHotspots(kShellSpotFlag);
+ }
+}
+
+bool PegasusEngine::isClickInput(const Input &input, const Hotspot *cursorSpot) {
+ if (_cursor->isVisible() && cursorSpot)
+ return JMPPPInput::isClickInput(input);
+ else
+ return false;
+}
+
+InputBits PegasusEngine::getClickFilter() {
+ return JMPPPInput::getClickInputFilter();
+}
+
+void PegasusEngine::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+ if (clickedSpot->getObjectID() == kCurrentItemSpotID) {
+ InventoryItem *currentItem = getCurrentInventoryItem();
+ if (currentItem) {
+ removeItemFromInventory(currentItem);
+ dragItem(input, currentItem, kDragInventoryUse);
+ }
+ } else if (clickedSpot->getObjectID() == kInfoReturnSpotID) {
+ toggleInfo();
+ }
+}
+
+InventoryResult PegasusEngine::removeItemFromInventory(InventoryItem *item) {
+ InventoryResult result;
+
+ if (g_interface)
+ result = g_interface->removeInventoryItem(item);
+ else
+ result = _items.removeItem(item);
+
+ // This should never happen
+ assert(result == kInventoryOK);
+
+ return result;
+}
+
+void PegasusEngine::removeAllItemsFromInventory() {
+ if (g_interface)
+ g_interface->removeAllItemsFromInventory();
+ else
+ _items.removeAllItems();
+}
+
+/////////////////////////////////////////////
+//
+// Biochip handling.
+
+// Adding biochips to the biochip drawer is a little funny, because of two things:
+// -- We get the map biochip and pegasus biochip at the same time by dragging
+// one sprite in TSA
+// -- We can drag in more than one copy of the various biochips.
+// Because of this we need to make sure that no more than one copy of each biochip
+// is ever added.
+
+InventoryResult PegasusEngine::addItemToBiochips(BiochipItem *biochip) {
+ InventoryResult result;
+
+ if (g_interface)
+ result = g_interface->addBiochip(biochip);
+ else
+ result = _biochips.addItem(biochip);
+
+ // This can never happen
+ assert(result == kInventoryOK);
+
+ GameState.setTakenItem(biochip, true);
+
+ if (g_neighborhood)
+ g_neighborhood->pickedUpItem(biochip);
+
+ g_AIArea->checkMiddleArea();
+
+ return result;
+}
+
+void PegasusEngine::removeAllItemsFromBiochips() {
+ if (g_interface)
+ g_interface->removeAllItemsFromBiochips();
+ else
+ _biochips.removeAllItems();
+}
+
+void PegasusEngine::setSoundFXLevel(uint16 fxLevel) {
+ _FXLevel = fxLevel;
+ if (_neighborhood)
+ _neighborhood->setSoundFXLevel(fxLevel);
+ if (g_AIArea)
+ g_AIArea->setAIVolume(fxLevel);
+}
+
+void PegasusEngine::setAmbienceLevel(uint16 ambientLevel) {
+ _ambientLevel = ambientLevel;
+ if (_neighborhood)
+ _neighborhood->setAmbienceLevel(ambientLevel);
+}
+
+void PegasusEngine::pauseMenu(bool menuUp) {
+ if (menuUp) {
+ pauseEngine(true);
+ _screenDimmer.startDisplaying();
+ _screenDimmer.show();
+ _gfx->updateDisplay();
+ useMenu(new PauseMenu());
+ } else {
+ pauseEngine(false);
+ _screenDimmer.hide();
+ _screenDimmer.stopDisplaying();
+ useMenu(0);
+ g_AIArea->checkMiddleArea();
+ }
+}
+
+void PegasusEngine::autoDragItemIntoRoom(Item *item, Sprite *draggingSprite) {
+ if (g_AIArea)
+ g_AIArea->lockAIOut();
+
+ Common::Point start, stop;
+ draggingSprite->getLocation(start.x, start.y);
+
+ Hotspot *dropSpot = _neighborhood->getItemScreenSpot(item, draggingSprite);
+
+ if (dropSpot) {
+ dropSpot->getCenter(stop.x, stop.y);
+ } else {
+ stop.x = kNavAreaLeft + 256;
+ stop.y = kNavAreaTop + 128;
+ }
+
+ Common::Rect bounds;
+ draggingSprite->getBounds(bounds);
+ stop.x -= bounds.width() >> 1;
+ stop.y -= bounds.height() >> 1;
+
+ int dx = ABS(stop.x - start.x);
+ int dy = ABS(stop.y - start.y);
+ TimeValue time = MAX(dx, dy);
+
+ allowInput(false);
+ _autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale);
+
+ while (_autoDragger.isDragging()) {
+ checkCallBacks();
+ refreshDisplay();
+ _system->delayMillis(10);
+ }
+
+ _neighborhood->dropItemIntoRoom(_draggingItem, dropSpot);
+ allowInput(true);
+ delete _draggingSprite;
+
+ if (g_AIArea)
+ g_AIArea->unlockAI();
+}
+
+void PegasusEngine::autoDragItemIntoInventory(Item *, Sprite *draggingSprite) {
+ if (g_AIArea)
+ g_AIArea->lockAIOut();
+
+ Common::Point start;
+ draggingSprite->getLocation(start.x, start.y);
+
+ Common::Rect r;
+ draggingSprite->getBounds(r);
+
+ Common::Point stop((76 + 172 - r.width()) / 2, 334 - (2 * r.height() / 3));
+
+ int dx = ABS(stop.x - start.x);
+ int dy = ABS(stop.y - start.y);
+ TimeValue time = MAX(dx, dy);
+
+ allowInput(false);
+ _autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale);
+
+ while (_autoDragger.isDragging()) {
+ checkCallBacks();
+ refreshDisplay();
+ _system->delayMillis(10);
+ }
+
+ addItemToInventory((InventoryItem *)_draggingItem);
+ allowInput(true);
+ delete _draggingSprite;
+
+ if (g_AIArea)
+ g_AIArea->unlockAI();
+}
+
+NeighborhoodID PegasusEngine::getCurrentNeighborhoodID() const {
+ if (_neighborhood)
+ return _neighborhood->getObjectID();
+
+ return kNoNeighborhoodID;
+}
+
+void PegasusEngine::pauseEngineIntern(bool pause) {
+ Engine::pauseEngineIntern(pause);
+
+ if (pause) {
+ for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++)
+ (*it)->pause();
+ } else {
+ for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++)
+ (*it)->resume();
+ }
+}
+
+uint PegasusEngine::getRandomBit() {
+ return _rnd->getRandomBit();
+}
+
+uint PegasusEngine::getRandomNumber(uint max) {
+ return _rnd->getRandomNumber(max);
+}
+
+void PegasusEngine::shuffleArray(int32 *arr, int32 count) {
+ if (count > 1) {
+ for (int32 i = 1; i < count; ++i) {
+ int32 j = _rnd->getRandomNumber(i);
+ if (j != i)
+ SWAP(arr[i], arr[j]);
+ }
+ }
+}
+
+void PegasusEngine::playEndMessage() {
+ if (g_interface) {
+ allowInput(false);
+ g_interface->playEndMessage();
+ allowInput(true);
+ }
+
+ die(kPlayerWonGame);
+}
+
+void PegasusEngine::doSubChase() {
+ Video::VideoDecoder *video = new Video::QuickTimeDecoder();
+ if (!video->loadFile("Images/Norad Alpha/Sub Chase Movie"))
+ error("Failed to load sub chase");
+
+ video->setEndTime(Audio::Timestamp(0, 133200, 600));
+ video->start();
+
+ while (!shouldQuit() && !video->endOfVideo()) {
+ if (video->needsUpdate()) {
+ const Graphics::Surface *frame = video->decodeNextFrame();
+
+ if (frame)
+ drawScaledFrame(frame, 0, 0);
+ }
+
+ Common::Event event;
+ while (_eventMan->pollEvent(event))
+ ;
+
+ _system->delayMillis(10);
+ }
+
+ delete video;
+}
+
+template<typename PixelInt>
+static void scaleFrame(const PixelInt *src, PixelInt *dst, int w, int h, int srcPitch) {
+ assert((srcPitch % sizeof(PixelInt)) == 0); // sanity check; allows some simpler code
+
+ PixelInt *dst1 = dst;
+ PixelInt *dst2 = dst + w * 2;
+
+ int srcInc = (srcPitch / sizeof(PixelInt)) - w;
+ int dstInc = w * 2;
+
+ while (h--) {
+ for (int x = 0; x < w; x++) {
+ PixelInt pixel = *src++;
+ *dst1++ = pixel;
+ *dst1++ = pixel;
+ *dst2++ = pixel;
+ *dst2++ = pixel;
+ }
+
+ src += srcInc;
+ dst1 += dstInc;
+ dst2 += dstInc;
+ }
+}
+
+void PegasusEngine::drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y) {
+ // Scale up the frame doing some simple scaling
+ Graphics::Surface scaledFrame;
+ scaledFrame.create(frame->w * 2, frame->h * 2, frame->format);
+
+ if (frame->format.bytesPerPixel == 2)
+ scaleFrame<uint16>((uint16 *)frame->pixels, (uint16 *)scaledFrame.pixels, frame->w, frame->h, frame->pitch);
+ else
+ scaleFrame<uint32>((uint32 *)frame->pixels, (uint32 *)scaledFrame.pixels, frame->w, frame->h, frame->pitch);
+
+ _system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h);
+ _system->updateScreen();
+ scaledFrame.free();
+}
+
+void PegasusEngine::destroyInventoryItem(const ItemID itemID) {
+ InventoryItem *item = (InventoryItem *)_allItems.findItemByID(itemID);
+
+ ItemExtraEntry entry;
+
+ switch (itemID) {
+ case kAirMask:
+ item->findItemExtra(kRemoveAirMask, entry);
+ item->setItemRoom(kMarsID, kMars49, kSouth);
+ break;
+ case kArgonCanister:
+ item->findItemExtra(kRemoveArgon, entry);
+ item->setItemRoom(kWSCID, kWSC02Morph, kSouth);
+ break;
+ case kCrowbar:
+ item->findItemExtra(kRemoveCrowbar, entry);
+ item->setItemRoom(kMarsID, kMars34, kSouth);
+ break;
+ case kJourneymanKey:
+ item->findItemExtra(kRemoveJourneymanKey, entry);
+ item->setItemRoom(kFullTSAID, kTSA22Red, kEast);
+ break;
+ case kMarsCard:
+ item->findItemExtra(kRemoveMarsCard, entry);
+ item->setItemRoom(kMarsID, kMars31South, kSouth);
+ break;
+ case kNitrogenCanister:
+ item->findItemExtra(kRemoveNitrogen, entry);
+ item->setItemRoom(kWSCID, kWSC02Messages, kSouth);
+ break;
+ case kOrangeJuiceGlassEmpty:
+ item->findItemExtra(kRemoveGlass, entry);
+ item->setItemRoom(kCaldoriaID, kCaldoriaReplicator, kNorth);
+ break;
+ case kPoisonDart:
+ item->findItemExtra(kRemoveDart, entry);
+ item->setItemRoom(kWSCID, kWSC01, kWest);
+ break;
+ case kSinclairKey:
+ item->findItemExtra(kRemoveSinclairKey, entry);
+ item->setItemRoom(kWSCID, kWSC02Morph, kSouth);
+ break;
+ default:
+ return;
+ }
+
+ g_interface->setCurrentInventoryItemID(itemID);
+ g_AIArea->playAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop);
+ removeItemFromInventory(item);
+}
+
+ItemID PegasusEngine::pickItemToDestroy() {
+/*
+ Must pick an item to destroy
+
+ Part I: Polite -- try to find an item that's been used
+ Part II: Desperate -- return the first available item.
+*/
+
+ // Polite:
+ if (playerHasItemID(kOrangeJuiceGlassEmpty))
+ return kOrangeJuiceGlassEmpty;
+ if (playerHasItemID(kPoisonDart)) {
+ if (GameState.getCurrentNeighborhood() != kWSCID ||
+ GameState.getWSCAnalyzedDart())
+ return kPoisonDart;
+ }
+ if (playerHasItemID(kJourneymanKey)) {
+ if (GameState.getTSAState() >= kTSAPlayerGotHistoricalLog &&
+ GameState.getTSAState() != kPlayerOnWayToPrehistoric &&
+ GameState.getTSAState() != kPlayerWentToPrehistoric)
+ return kJourneymanKey;
+ }
+ if (playerHasItemID(kMarsCard)) {
+ if (GameState.getCurrentNeighborhood() != kMarsID || GameState.getMarsArrivedBelow())
+ return kMarsCard;
+ }
+
+ // Don't want to deal with deleting the sinclair key and argon canister, since it's
+ // impossible to pick them up one at a time.
+
+ if (playerHasItemID(kNitrogenCanister)) {
+ if (GameState.getScoringGotCardBomb() && GameState.getCurrentNeighborhood() != kMarsID)
+ return kNitrogenCanister;
+ }
+ if (playerHasItemID(kCrowbar)) {
+ if (GameState.getCurrentNeighborhood() == kWSCID) {
+ if (GameState.getCurrentRoom() >= kWSC62)
+ return kCrowbar;
+ } else if (GameState.getCurrentNeighborhood() == kMarsID) {
+ if (GameState.getScoringGotCardBomb())
+ return kCrowbar;
+ } else
+ return kCrowbar;
+ }
+ if (playerHasItemID(kAirMask)) {
+ if (GameState.getCurrentNeighborhood() == kMarsID) {
+ if (g_neighborhood->getAirQuality(GameState.getCurrentRoom()) == kAirQualityGood)
+ return kAirMask;
+ } else if (GameState.getCurrentNeighborhood() != kNoradAlphaID &&
+ GameState.getCurrentNeighborhood() != kNoradDeltaID) {
+ return kAirMask;
+ }
+ }
+
+ // Desperate:
+ if (playerHasItemID(kPoisonDart))
+ return kPoisonDart;
+ if (playerHasItemID(kJourneymanKey))
+ return kJourneymanKey;
+ if (playerHasItemID(kMarsCard))
+ return kMarsCard;
+ if (playerHasItemID(kNitrogenCanister))
+ return kNitrogenCanister;
+ if (playerHasItemID(kCrowbar))
+ return kCrowbar;
+ if (playerHasItemID(kAirMask))
+ return kAirMask;
+
+ // Should never get this far...
+ error("Could not find item to delete");
+
+ return kNoItemID;
+}
+
+uint PegasusEngine::getNeighborhoodCD(const NeighborhoodID neighborhood) const {
+ switch (neighborhood) {
+ case kCaldoriaID:
+ case kNoradAlphaID:
+ case kNoradSubChaseID:
+ return 1;
+ case kFullTSAID:
+ case kPrehistoricID:
+ return 2;
+ case kMarsID:
+ return 3;
+ case kWSCID:
+ case kNoradDeltaID:
+ return 4;
+ case kTinyTSAID:
+ // Tiny TSA exists on three of the CD's, so just continue
+ // with the CD we're on
+ return _currentCD;
+ }
+
+ // Can't really happen, but it's a good fallback anyway :P
+ return 1;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h
new file mode 100644
index 0000000000..2a8ba22470
--- /dev/null
+++ b/engines/pegasus/pegasus.h
@@ -0,0 +1,327 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_H
+#define PEGASUS_H
+
+#include "common/list.h"
+#include "common/macresman.h"
+#include "common/rect.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/util.h"
+
+#include "engines/engine.h"
+
+#include "pegasus/graphics.h"
+#include "pegasus/hotspot.h"
+#include "pegasus/input.h"
+#include "pegasus/notification.h"
+#include "pegasus/timers.h"
+#include "pegasus/items/autodragger.h"
+#include "pegasus/items/inventory.h"
+#include "pegasus/items/itemdragger.h"
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Common {
+ class RandomSource;
+}
+
+namespace Video {
+ class VideoDecoder;
+}
+
+namespace Pegasus {
+
+class PegasusConsole;
+struct PegasusGameDescription;
+class SoundManager;
+class GraphicsManager;
+class Idler;
+class Cursor;
+class TimeBase;
+class GameMenu;
+class InventoryItem;
+class BiochipItem;
+class Neighborhood;
+
+class PegasusEngine : public ::Engine, public InputHandler, public NotificationManager {
+friend class InputHandler;
+
+public:
+ PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc);
+ virtual ~PegasusEngine();
+
+ // Engine stuff
+ const PegasusGameDescription *_gameDescription;
+ bool hasFeature(EngineFeature f) const;
+ GUI::Debugger *getDebugger();
+ bool canLoadGameStateCurrently();
+ bool canSaveGameStateCurrently();
+ Common::Error loadGameState(int slot);
+ Common::Error saveGameState(int slot, const Common::String &desc);
+
+ // Base classes
+ GraphicsManager *_gfx;
+ Common::MacResManager *_resFork;
+ Cursor *_cursor;
+
+ // Menu
+ void useMenu(GameMenu *menu);
+ bool checkGameMenu();
+
+ // Misc.
+ bool isDemo() const;
+ void addIdler(Idler *idler);
+ void removeIdler(Idler *idler);
+ void addTimeBase(TimeBase *timeBase);
+ void removeTimeBase(TimeBase *timeBase);
+ void delayShell(TimeValue time, TimeScale scale);
+ void resetIntroTimer();
+ void introTimerExpired();
+ void refreshDisplay();
+ bool playerAlive();
+ void processShell();
+ void checkCallBacks();
+ void createInterface();
+ void setGameMode(const GameMode);
+ GameMode getGameMode() const { return _gameMode; }
+ uint getRandomBit();
+ uint getRandomNumber(uint max);
+ void shuffleArray(int32 *arr, int32 count);
+ void drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y);
+ HotspotList &getAllHotspots() { return _allHotspots; }
+
+ // Energy
+ void setLastEnergyValue(const int32 value) { _savedEnergyValue = value; }
+ int32 getSavedEnergyValue() { return _savedEnergyValue; }
+
+ // Death
+ void setEnergyDeathReason(const DeathReason reason) { _deathReason = reason; }
+ DeathReason getEnergyDeathReason() { return _deathReason; }
+ void resetEnergyDeathReason();
+ void die(const DeathReason);
+ void playEndMessage();
+
+ // Volume
+ uint16 getSoundFXLevel() { return _FXLevel; }
+ void setSoundFXLevel(uint16);
+ uint16 getAmbienceLevel() { return _ambientLevel; }
+ void setAmbienceLevel(uint16);
+
+ // Items
+ ItemList &getAllItems() { return _allItems; }
+ bool playerHasItem(const Item *);
+ bool playerHasItemID(const ItemID);
+ void checkFlashlight();
+ bool itemInLocation(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+
+ // Inventory Items
+ InventoryItem *getCurrentInventoryItem();
+ bool itemInInventory(InventoryItem *);
+ bool itemInInventory(ItemID);
+ Inventory *getItemsInventory() { return &_items; }
+ InventoryResult addItemToInventory(InventoryItem *);
+ void removeAllItemsFromInventory();
+ InventoryResult removeItemFromInventory(InventoryItem *);
+ uint32 countInventoryItems() { return _items.getNumItems(); }
+
+ // Biochips
+ BiochipItem *getCurrentBiochip();
+ bool itemInBiochips(BiochipItem *);
+ bool itemInBiochips(ItemID);
+ Inventory *getBiochipsInventory() { return &_biochips; }
+ void removeAllItemsFromBiochips();
+ InventoryResult addItemToBiochips(BiochipItem *);
+
+ // AI
+ Common::String getBriefingMovie();
+ Common::String getEnvScanMovie();
+ uint getNumHints();
+ Common::String getHintMovie(uint);
+ bool canSolve();
+ void prepareForAIHint(const Common::String &);
+ void cleanUpAfterAIHint(const Common::String &);
+ Common::SeekableReadStream *_aiSaveStream;
+
+ // Neighborhood
+ void jumpToNewEnvironment(const NeighborhoodID, const RoomID, const DirectionConstant);
+ NeighborhoodID getCurrentNeighborhoodID() const;
+
+ // Dragging
+ void dragItem(const Input &, Item *, DragType);
+ bool isDragging() const { return _dragType != kDragNoDrag; }
+ DragType getDragType() const { return _dragType; }
+ Item *getDraggingItem() const { return _draggingItem; }
+ void dragTerminated(const Input &);
+ void autoDragItemIntoRoom(Item *, Sprite *);
+ void autoDragItemIntoInventory(Item *, Sprite*);
+
+ // Save/Load
+ void makeContinuePoint();
+ bool swapSaveAllowed(bool allow) {
+ bool old = _saveAllowed;
+ _saveAllowed = allow;
+ return old;
+ }
+ bool swapLoadAllowed(bool allow) {
+ bool old = _loadAllowed;
+ _loadAllowed = allow;
+ return old;
+ }
+ void requestSave() { _saveRequested = true; }
+ bool saveRequested() const { return _saveRequested; }
+ void requestLoad() { _loadRequested = true; }
+ bool loadRequested() const { return _loadRequested; }
+
+protected:
+ Common::Error run();
+ void pauseEngineIntern(bool pause);
+
+ Notification _shellNotification;
+ virtual void receiveNotification(Notification *notification, const NotificationFlags flags);
+
+ void handleInput(const Input &input, const Hotspot *cursorSpot);
+ virtual bool isClickInput(const Input &, const Hotspot *);
+ virtual InputBits getClickFilter();
+
+ void clickInHotspot(const Input &, const Hotspot *);
+ void activateHotspots(void);
+
+ void updateCursor(const Common::Point, const Hotspot *);
+ bool wantsCursor();
+
+private:
+ // Console
+ PegasusConsole *_console;
+
+ // Intro
+ void runIntro();
+ void stopIntroTimer();
+ bool detectOpeningClosingDirectory();
+ Common::String _introDirectory;
+ FuseFunction *_introTimer;
+
+ // Idlers
+ Idler *_idlerHead;
+ void giveIdleTime();
+
+ // Items
+ ItemList _allItems;
+ void createItems();
+ void createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction);
+ Inventory _items;
+ Inventory _biochips;
+ ItemID _currentItemID;
+ ItemID _currentBiochipID;
+ void destroyInventoryItem(const ItemID itemID);
+ ItemID pickItemToDestroy();
+
+ // TimeBases
+ Common::List<TimeBase *> _timeBases;
+
+ // Save/Load
+ bool loadFromStream(Common::ReadStream *stream);
+ bool writeToStream(Common::WriteStream *stream, int saveType);
+ void loadFromContinuePoint();
+ void writeContinueStream(Common::WriteStream *stream);
+ Common::SeekableReadStream *_continuePoint;
+ bool _saveAllowed, _loadAllowed; // It's so nice that this was in the original code already :P
+ Common::Error showLoadDialog();
+ Common::Error showSaveDialog();
+ bool _saveRequested, _loadRequested;
+
+ // Misc.
+ Hotspot _returnHotspot;
+ HotspotList _allHotspots;
+ InputHandler *_savedHandler;
+ void showTempScreen(const Common::String &fileName);
+ bool playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16 y);
+ void throwAwayEverything();
+ void shellGameInput(const Input &input, const Hotspot *cursorSpot);
+ Common::RandomSource *_rnd;
+ void doSubChase();
+ uint getNeighborhoodCD(const NeighborhoodID neighborhood) const;
+ uint _currentCD;
+
+ // Menu
+ GameMenu *_gameMenu;
+ void doGameMenuCommand(const GameMenuCommand);
+ void doInterfaceOverview();
+ ScreenDimmer _screenDimmer;
+ void pauseMenu(bool menuUp);
+
+ // Energy
+ int32 _savedEnergyValue;
+
+ // Death
+ DeathReason _deathReason;
+ void doDeath();
+
+ // Neighborhood
+ Neighborhood *_neighborhood;
+ void useNeighborhood(Neighborhood *neighborhood);
+ void performJump(NeighborhoodID start);
+ void startNewGame();
+ void startNeighborhood();
+ void makeNeighborhood(NeighborhoodID, Neighborhood *&);
+
+ // Sound
+ uint16 _ambientLevel;
+ uint16 _FXLevel;
+
+ // Game Mode
+ GameMode _gameMode;
+ bool _switchModesSync;
+ void switchGameMode(const GameMode, const GameMode);
+ bool canSwitchGameMode(const GameMode, const GameMode);
+
+ // Dragging
+ ItemDragger _itemDragger;
+ Item *_draggingItem;
+ Sprite *_draggingSprite;
+ DragType _dragType;
+ AutoDragger _autoDragger;
+
+ // Interface
+ void toggleInventoryDisplay();
+ void toggleBiochipDisplay();
+ void raiseInventoryDrawer();
+ void raiseBiochipDrawer();
+ void lowerInventoryDrawer();
+ void lowerBiochipDrawer();
+ void raiseInventoryDrawerSync();
+ void raiseBiochipDrawerSync();
+ void lowerInventoryDrawerSync();
+ void lowerBiochipDrawerSync();
+ void showInfoScreen();
+ void hideInfoScreen();
+ void toggleInfo();
+ Movie _bigInfoMovie, _smallInfoMovie;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/scoring.h b/engines/pegasus/scoring.h
new file mode 100644
index 0000000000..fbf8641ecb
--- /dev/null
+++ b/engines/pegasus/scoring.h
@@ -0,0 +1,281 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_SCORING_H
+#define PEGASUS_SCORING_H
+
+#include "pegasus/types.h"
+
+namespace Pegasus {
+
+/////////////////////////////////////////////
+//
+// Scoring.
+
+static const CoordType kDeathScreenScoreLeft = 151;
+static const CoordType kDeathScreenScoreTop = 212;
+static const CoordType kDeathScreenScoreWidth = 124;
+static const CoordType kDeathScreenScoreHeight = 12;
+static const CoordType kDeathScreenScoreSkipVert = -16;
+
+// Caldoria & TSA
+
+static const GameScoreType kSawINNScore = 5;
+static const GameScoreType kTookShowerScore = 2;
+static const GameScoreType kFixedHairScore = 2;
+static const GameScoreType kGotKeyCardScore = 5;
+static const GameScoreType kReadPaperScore = 2;
+static const GameScoreType kLookThroughTelescopeScore = 2;
+static const GameScoreType kSawCaldoriaKioskScore = 2;
+static const GameScoreType kGoToTSAScore = 3;
+
+static const GameScoreType kEnterTSAScore = 2;
+static const GameScoreType kSawBust1Score = 2;
+static const GameScoreType kSawBust2Score = 2;
+static const GameScoreType kSawBust3Score = 2;
+static const GameScoreType kSawBust4Score = 2;
+static const GameScoreType kSawBust5Score = 2;
+static const GameScoreType kSawBust6Score = 2;
+static const GameScoreType kSawTheoryScore = 4;
+static const GameScoreType kSawBackgroundScore = 4;
+static const GameScoreType kSawProcedureScore = 4;
+static const GameScoreType kGotJourneymanKeyScore = 5;
+static const GameScoreType kGotPegasusBiochipScore = 5;
+static const GameScoreType kGotBiosuitScore = 5;
+static const GameScoreType kGoToPrehistoricScore = 5;
+
+static const GameScoreType kPutLogInReaderScore = 5;
+static const GameScoreType kSawCaldoriaNormalScore = 2;
+static const GameScoreType kSawCaldoriaAlteredScore = 2;
+static const GameScoreType kSawNoradNormalScore = 2;
+static const GameScoreType kSawNoradAlteredScore = 2;
+static const GameScoreType kSawMarsNormalScore = 2;
+static const GameScoreType kSawMarsAlteredScore = 2;
+static const GameScoreType kSawWSCNormalScore = 2;
+static const GameScoreType kSawWSCAlteredScore = 2;
+static const GameScoreType kWentToReadyRoom2Score = 5;
+static const GameScoreType kWentAfterSinclairScore = 5;
+static const GameScoreType kUsedCardBombScore = 10;
+static const GameScoreType kShieldedCardBombScore = 5;
+static const GameScoreType kStunnedSinclairScore = 10;
+static const GameScoreType kDisarmedNukeScore = 10;
+
+static const GameScoreType kMaxCaldoriaTSAScoreBefore = kSawINNScore +
+ kTookShowerScore +
+ kFixedHairScore +
+ kGotKeyCardScore +
+ kReadPaperScore +
+ kLookThroughTelescopeScore +
+ kSawCaldoriaKioskScore +
+ kGoToTSAScore +
+ kEnterTSAScore +
+ kSawBust1Score +
+ kSawBust2Score +
+ kSawBust3Score +
+ kSawBust4Score +
+ kSawBust5Score +
+ kSawBust6Score +
+ kSawTheoryScore +
+ kSawBackgroundScore +
+ kSawProcedureScore +
+ kGotJourneymanKeyScore +
+ kGotPegasusBiochipScore +
+ kGotBiosuitScore +
+ kGoToPrehistoricScore +
+ kPutLogInReaderScore +
+ kSawCaldoriaNormalScore +
+ kSawCaldoriaAlteredScore +
+ kSawNoradNormalScore +
+ kSawNoradAlteredScore +
+ kSawMarsNormalScore +
+ kSawMarsAlteredScore +
+ kSawWSCNormalScore +
+ kSawWSCAlteredScore +
+ kWentToReadyRoom2Score;
+
+static const GameScoreType kMaxCaldoriaTSAScoreAfter = kWentAfterSinclairScore +
+ kUsedCardBombScore +
+ kShieldedCardBombScore +
+ kStunnedSinclairScore +
+ kDisarmedNukeScore;
+
+static const GameScoreType kMaxCaldoriaTSAScore = kMaxCaldoriaTSAScoreBefore +
+ kMaxCaldoriaTSAScoreAfter;
+
+// Prehistoric
+
+static const GameScoreType kThrewBreakerScore = 10;
+static const GameScoreType kExtendedBridgeScore = 10;
+static const GameScoreType kGotHistoricalLogScore = 5;
+static const GameScoreType kFinishedPrehistoricScore = 10;
+
+static const GameScoreType kMaxPrehistoricScore = kThrewBreakerScore +
+ kExtendedBridgeScore +
+ kGotHistoricalLogScore +
+ kFinishedPrehistoricScore;
+
+// Mars
+
+static const GameScoreType kThrownByRobotScore = 3;
+static const GameScoreType kGotMarsCardScore = 5;
+static const GameScoreType kSawMarsKioskScore = 2;
+static const GameScoreType kSawTransportMapScore = 2;
+static const GameScoreType kGotCrowBarScore = 5;
+static const GameScoreType kTurnedOnTransportScore = 5;
+static const GameScoreType kGotOxygenMaskScore = 5;
+static const GameScoreType kAvoidedRobotScore = 5;
+static const GameScoreType kActivatedPlatformScore = 2;
+static const GameScoreType kUsedLiquidNitrogenScore = 3;
+static const GameScoreType kUsedCrowBarScore = 3;
+static const GameScoreType kFoundCardBombScore = 4;
+static const GameScoreType kDisarmedCardBombScore = 8;
+static const GameScoreType kGotCardBombScore = 5;
+static const GameScoreType kThreadedMazeScore = 5;
+static const GameScoreType kThreadedGearRoomScore = 2;
+static const GameScoreType kEnteredShuttleScore = 2;
+static const GameScoreType kEnteredLaunchTubeScore = 4;
+static const GameScoreType kStoppedRobotsShuttleScore = 10;
+static const GameScoreType kGotMarsOpMemChipScore = 10;
+static const GameScoreType kFinishedMarsScore = 10;
+
+static const GameScoreType kMaxMarsScore = kThrownByRobotScore +
+ kGotMarsCardScore +
+ kSawMarsKioskScore +
+ kSawTransportMapScore +
+ kGotCrowBarScore +
+ kTurnedOnTransportScore +
+ kGotOxygenMaskScore +
+ kAvoidedRobotScore +
+ kActivatedPlatformScore +
+ kUsedLiquidNitrogenScore +
+ kUsedCrowBarScore +
+ kFoundCardBombScore +
+ kDisarmedCardBombScore +
+ kGotCardBombScore +
+ kThreadedMazeScore +
+ kThreadedGearRoomScore +
+ kEnteredShuttleScore +
+ kEnteredLaunchTubeScore +
+ kStoppedRobotsShuttleScore +
+ kGotMarsOpMemChipScore +
+ kFinishedMarsScore;
+
+// Norad
+
+static const GameScoreType kSawSecurityMonitorScore = 5;
+static const GameScoreType kFilledOxygenCanisterScore = 5;
+static const GameScoreType kFilledArgonCanisterScore = 5;
+static const GameScoreType kSawUnconsciousOperatorScore = 5;
+static const GameScoreType kWentThroughPressureDoorScore = 5;
+static const GameScoreType kPreppedSubScore = 5;
+static const GameScoreType kEnteredSubScore = 5;
+static const GameScoreType kExitedSubScore = 10;
+static const GameScoreType kSawRobotAt54NorthScore = 5;
+static const GameScoreType kPlayedWithClawScore = 5;
+static const GameScoreType kUsedRetinalChipScore = 5;
+static const GameScoreType kFinishedGlobeGameScore = 10;
+static const GameScoreType kStoppedNoradRobotScore = 10;
+static const GameScoreType kGotNoradOpMemChipScore = 10;
+static const GameScoreType kFinishedNoradScore = 10;
+
+static const GameScoreType kMaxNoradScore = kSawSecurityMonitorScore +
+ kFilledOxygenCanisterScore +
+ kFilledArgonCanisterScore +
+ kSawUnconsciousOperatorScore +
+ kWentThroughPressureDoorScore +
+ kPreppedSubScore +
+ kEnteredSubScore +
+ kExitedSubScore +
+ kSawRobotAt54NorthScore +
+ kPlayedWithClawScore +
+ kUsedRetinalChipScore +
+ kFinishedGlobeGameScore +
+ kStoppedNoradRobotScore +
+ kGotNoradOpMemChipScore +
+ kFinishedNoradScore;
+
+// WSC
+
+static const GameScoreType kRemovedDartScore = 5;
+static const GameScoreType kAnalyzedDartScore = 5;
+static const GameScoreType kBuiltAntidoteScore = 5;
+static const GameScoreType kGotSinclairKeyScore = 5;
+static const GameScoreType kGotArgonCanisterScore = 5;
+static const GameScoreType kGotNitrogenCanisterScore = 5;
+static const GameScoreType kPlayedWithMessagesScore = 2;
+static const GameScoreType kSawMorphExperimentScore = 3;
+static const GameScoreType kEnteredSinclairOfficeScore = 2;
+static const GameScoreType kSawBrochureScore = 3;
+static const GameScoreType kSawSinclairEntry1Score = 3;
+static const GameScoreType kSawSinclairEntry2Score = 3;
+static const GameScoreType kSawSinclairEntry3Score = 3;
+static const GameScoreType kSawWSCDirectoryScore = 3;
+static const GameScoreType kUsedCrowBarInWSCScore = 5;
+static const GameScoreType kFinishedPlasmaDodgeScore = 10;
+static const GameScoreType kOpenedCatwalkScore = 3;
+static const GameScoreType kStoppedWSCRobotScore = 10;
+static const GameScoreType kGotWSCOpMemChipScore = 10;
+static const GameScoreType kFinishedWSCScore = 10;
+
+static const GameScoreType kMaxWSCScore = kRemovedDartScore +
+ kAnalyzedDartScore +
+ kBuiltAntidoteScore +
+ kGotSinclairKeyScore +
+ kGotArgonCanisterScore +
+ kGotNitrogenCanisterScore +
+ kPlayedWithMessagesScore +
+ kSawMorphExperimentScore +
+ kEnteredSinclairOfficeScore +
+ kSawBrochureScore +
+ kSawSinclairEntry1Score +
+ kSawSinclairEntry2Score +
+ kSawSinclairEntry3Score +
+ kSawWSCDirectoryScore +
+ kUsedCrowBarInWSCScore +
+ kFinishedPlasmaDodgeScore +
+ kOpenedCatwalkScore +
+ kStoppedWSCRobotScore +
+ kGotWSCOpMemChipScore +
+ kFinishedWSCScore;
+
+// Gandhi
+
+static const GameScoreType kMarsGandhiScore = 10;
+static const GameScoreType kNoradGandhiScore = 10;
+static const GameScoreType kWSCGandhiScore = 10;
+
+static const GameScoreType kMaxGandhiScore = kMarsGandhiScore +
+ kNoradGandhiScore +
+ kWSCGandhiScore;
+
+static const GameScoreType kMaxTotalScore = kMaxCaldoriaTSAScore +
+ kMaxPrehistoricScore +
+ kMaxMarsScore +
+ kMaxNoradScore +
+ kMaxWSCScore +
+ kMaxGandhiScore;
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/sound.cpp b/engines/pegasus/sound.cpp
new file mode 100644
index 0000000000..bf15858e80
--- /dev/null
+++ b/engines/pegasus/sound.cpp
@@ -0,0 +1,181 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/audiostream.h"
+#include "audio/decoders/aiff.h"
+#include "audio/decoders/quicktime.h"
+#include "common/file.h"
+#include "common/system.h"
+
+#include "pegasus/fader.h"
+#include "pegasus/sound.h"
+
+namespace Pegasus {
+
+Sound::Sound() {
+ _stream = 0;
+ _volume = 0xFF;
+ _fader = 0;
+}
+
+Sound::~Sound() {
+ disposeSound();
+}
+
+void Sound::disposeSound() {
+ stopSound();
+ delete _stream; _stream = 0;
+}
+
+void Sound::initFromAIFFFile(const Common::String &fileName) {
+ disposeSound();
+
+ Common::File *file = new Common::File();
+ if (!file->open(fileName)) {
+ warning("Failed to open AIFF file '%s'", fileName.c_str());
+ delete file;
+ return;
+ }
+
+ _stream = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
+}
+
+void Sound::initFromQuickTime(const Common::String &fileName) {
+ disposeSound();
+
+ _stream = Audio::makeQuickTimeStream(fileName);
+
+ if (!_stream)
+ warning("Failed to open QuickTime file '%s'", fileName.c_str());
+}
+
+void Sound::attachFader(SoundFader *fader) {
+ if (_fader)
+ _fader->attachSound(0);
+
+ _fader = fader;
+
+ if (_fader)
+ _fader->attachSound(this);
+}
+
+void Sound::playSound() {
+ if (!isSoundLoaded())
+ return;
+
+ stopSound();
+
+ if (_fader)
+ setVolume(_fader->getFaderValue());
+
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, _stream, -1, _volume, 0, DisposeAfterUse::NO);
+}
+
+void Sound::loopSound() {
+ if (!isSoundLoaded())
+ return;
+
+ stopSound();
+
+ // Create a looping stream
+ Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO);
+
+ // Assume that if there is a fader, we're going to fade the sound in.
+ if (_fader)
+ setVolume(0);
+
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES);
+}
+
+void Sound::playSoundSegment(uint32 start, uint32 end) {
+ if (!isSoundLoaded())
+ return;
+
+ stopSound();
+
+ Audio::AudioStream *subStream = new Audio::SubSeekableAudioStream(_stream, Audio::Timestamp(0, start, 600), Audio::Timestamp(0, end, 600), DisposeAfterUse::NO);
+
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, subStream, -1, _volume, 0, DisposeAfterUse::YES);
+}
+
+void Sound::stopSound() {
+ g_system->getMixer()->stopHandle(_handle);
+}
+
+void Sound::setVolume(const uint16 volume) {
+ // Clipping the volume to [0x00, 0xFF] instead of Apple's [0, 0x100]
+ // We store the volume in case SetVolume is called before the sound starts
+
+ _volume = (volume == 0x100) ? 0xFF : volume;
+ g_system->getMixer()->setChannelVolume(_handle, _volume);
+}
+
+bool Sound::isPlaying() {
+ return isSoundLoaded() && g_system->getMixer()->isSoundHandleActive(_handle);
+}
+
+bool Sound::isSoundLoaded() const {
+ return _stream != 0;
+}
+
+SoundTimeBase::SoundTimeBase() {
+ setScale(600);
+ _startScale = 600;
+ _stopScale = 600;
+ _setToStart = false;
+}
+
+void SoundTimeBase::playSoundSegment(uint32 startTime, uint32 endTime) {
+ _startTime = startTime;
+ _stopTime = endTime;
+ _setToStart = true;
+ _time = Common::Rational(startTime, getScale());
+ setRate(1);
+ Sound::playSoundSegment(startTime, endTime);
+}
+
+void SoundTimeBase::updateTime() {
+ if (_setToStart) {
+ if (isPlaying()) {
+ // Not at the end, let's get the time
+ uint numFrames = g_system->getMixer()->getSoundElapsedTime(_handle) * 600 / 1000;
+
+ // WORKAROUND: Our mixer is woefully inaccurate and quite often returns
+ // times that exceed the actual length of the clip. We'll just fake times
+ // that are under the final time to ensure any trigger for the end time is
+ // only sent when the sound has actually stopped.
+ if (numFrames >= (_stopTime - _startTime))
+ numFrames = _stopTime - _startTime - 1;
+
+ _time = Common::Rational(_startTime + numFrames, getScale());
+ } else {
+ // Assume we reached the end
+ _setToStart = false;
+ _time = Common::Rational(_stopTime, getScale());
+ }
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/sound.h b/engines/pegasus/sound.h
new file mode 100644
index 0000000000..57cfd52e41
--- /dev/null
+++ b/engines/pegasus/sound.h
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_SOUND_H
+#define PEGASUS_SOUND_H
+
+#include "audio/mixer.h"
+#include "common/str.h"
+#include "pegasus/timers.h"
+
+namespace Audio {
+ class SeekableAudioStream;
+}
+
+namespace Pegasus {
+
+class SoundFader;
+
+// Things you might want to do with sound:
+// - Start it
+// - Stop it
+// - Loop it
+// - Pause it
+// - Set the volume
+// - Set the pitch (rate)
+// - Pan the sound
+// - Change these settings dynamically over time
+
+class Sound {
+public:
+ Sound();
+ ~Sound();
+
+ // We only have one access point here because we should
+ // only be opening an AIFF file from a file name. We're
+ // not using the resource fork string resources.
+ void initFromAIFFFile(const Common::String &fileName);
+
+ // Unlike the original game, we're going to use a regular
+ // audio stream for sound spots. The original treated them
+ // as movies.
+ void initFromQuickTime(const Common::String &fileName);
+
+ void disposeSound();
+ bool isSoundLoaded() const;
+ void playSound();
+ void loopSound();
+ void playSoundSegment(uint32 start, uint32 end);
+ void stopSound();
+ void setVolume(const uint16 volume);
+ bool isPlaying();
+
+ void attachFader(SoundFader *fader);
+
+protected:
+ Audio::SeekableAudioStream *_stream;
+ Audio::SoundHandle _handle;
+ byte _volume;
+
+ SoundFader *_fader;
+};
+
+// TODO: Make this class follow TimeBase better
+// Right now it's just a loose wrapper to plug callbacks
+// into sounds. Since this is only used for spot sounds,
+// I'm not too worried about it right now as its usage
+// is very limited.
+// At the very least, the regular TimeBase functions for
+// setting/getting should be neutered.
+class SoundTimeBase : public Sound, public TimeBase {
+public:
+ SoundTimeBase();
+ ~SoundTimeBase() {}
+
+ void playSoundSegment(uint32 start, uint32 end);
+
+protected:
+ void updateTime();
+
+private:
+ bool _setToStart;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/surface.cpp b/engines/pegasus/surface.cpp
new file mode 100644
index 0000000000..e9e0958f9d
--- /dev/null
+++ b/engines/pegasus/surface.cpp
@@ -0,0 +1,392 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/file.h"
+#include "common/macresman.h"
+#include "common/stream.h"
+#include "common/system.h"
+#include "graphics/surface.h"
+#include "graphics/decoders/pict.h"
+#include "video/video_decoder.h"
+
+#include "pegasus/pegasus.h"
+#include "pegasus/surface.h"
+
+namespace Pegasus {
+
+Surface::Surface() {
+ _ownsSurface = false;
+ _surface = 0;
+}
+
+Surface::~Surface() {
+ deallocateSurface();
+}
+
+void Surface::deallocateSurface() {
+ if (_surface) {
+ if (_ownsSurface) {
+ _surface->free();
+ delete _surface;
+ }
+
+ _surface = 0;
+ _bounds = Common::Rect();
+ _ownsSurface = false;
+ }
+}
+
+void Surface::shareSurface(Surface *surface) {
+ deallocateSurface();
+
+ if (surface) {
+ _ownsSurface = false;
+ _surface = surface->getSurface();
+ surface->getSurfaceBounds(_bounds);
+ }
+}
+
+void Surface::allocateSurface(const Common::Rect &bounds) {
+ deallocateSurface();
+
+ if (bounds.isEmpty())
+ return;
+
+ _bounds = bounds;
+ _surface = new Graphics::Surface();
+ _surface->create(bounds.width(), bounds.height(), g_system->getScreenFormat());
+ _ownsSurface = true;
+}
+
+void Surface::getImageFromPICTFile(const Common::String &fileName) {
+ Common::File pict;
+ if (!pict.open(fileName))
+ error("Could not open picture '%s'", fileName.c_str());
+
+ getImageFromPICTStream(&pict);
+}
+
+void Surface::getImageFromPICTResource(Common::MacResManager *resFork, uint16 id) {
+ Common::SeekableReadStream *res = resFork->getResource(MKTAG('P', 'I', 'C', 'T'), id);
+ if (!res)
+ error("Could not open PICT resource %d from '%s'", id, resFork->getBaseFileName().c_str());
+
+ getImageFromPICTStream(res);
+ delete res;
+}
+
+void Surface::getImageFromPICTStream(Common::SeekableReadStream *stream) {
+ Graphics::PICTDecoder pict;
+
+ if (!pict.loadStream(*stream))
+ error("Failed to load PICT image");
+
+ _surface = pict.getSurface()->convertTo(g_system->getScreenFormat(), pict.getPalette());
+ _ownsSurface = true;
+ _bounds = Common::Rect(0, 0, _surface->w, _surface->h);
+}
+
+void Surface::getImageFromMovieFrame(Video::VideoDecoder *video, TimeValue time) {
+ video->seek(Audio::Timestamp(0, time, 600));
+ const Graphics::Surface *frame = video->decodeNextFrame();
+
+ if (frame) {
+ if (!_surface)
+ _surface = new Graphics::Surface();
+
+ _surface->copyFrom(*frame);
+ _ownsSurface = true;
+ _bounds = Common::Rect(0, 0, _surface->w, _surface->h);
+ } else {
+ deallocateSurface();
+ }
+}
+
+void Surface::copyToCurrentPort() const {
+ copyToCurrentPort(_bounds);
+}
+
+void Surface::copyToCurrentPortTransparent() const {
+ copyToCurrentPortTransparent(_bounds);
+}
+
+void Surface::copyToCurrentPort(const Common::Rect &rect) const {
+ copyToCurrentPort(rect, rect);
+}
+
+void Surface::copyToCurrentPortTransparent(const Common::Rect &rect) const {
+ copyToCurrentPortTransparent(rect, rect);
+}
+
+void Surface::copyToCurrentPort(const Common::Rect &srcRect, const Common::Rect &dstRect) const {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface();
+ byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top);
+ byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top);
+
+ int lineSize = srcRect.width() * _surface->format.bytesPerPixel;
+
+ for (int y = 0; y < srcRect.height(); y++) {
+ memcpy(dst, src, lineSize);
+ src += _surface->pitch;
+ dst += screen->pitch;
+ }
+}
+
+void Surface::copyToCurrentPortTransparent(const Common::Rect &srcRect, const Common::Rect &dstRect) const {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface();
+ byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top);
+ byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top);
+
+ int lineSize = srcRect.width() * _surface->format.bytesPerPixel;
+
+ for (int y = 0; y < srcRect.height(); y++) {
+ for (int x = 0; x < srcRect.width(); x++) {
+ if (g_system->getScreenFormat().bytesPerPixel == 2) {
+ uint16 color = READ_UINT16(src);
+ if (!isTransparent(color))
+ memcpy(dst, src, 2);
+ } else if (g_system->getScreenFormat().bytesPerPixel == 4) {
+ uint32 color = READ_UINT32(src);
+ if (!isTransparent(color))
+ memcpy(dst, src, 4);
+ }
+
+ src += g_system->getScreenFormat().bytesPerPixel;
+ dst += g_system->getScreenFormat().bytesPerPixel;
+ }
+
+ src += _surface->pitch - lineSize;
+ dst += screen->pitch - lineSize;
+ }
+}
+
+void Surface::copyToCurrentPortMasked(const Common::Rect &srcRect, const Common::Rect &dstRect, const Surface *mask) const {
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface();
+ byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top);
+ byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top);
+
+ int lineSize = srcRect.width() * _surface->format.bytesPerPixel;
+
+ for (int y = 0; y < srcRect.height(); y++) {
+ byte *maskSrc = (byte *)mask->getSurface()->getBasePtr(0, y);
+
+ for (int x = 0; x < srcRect.width(); x++) {
+ if (g_system->getScreenFormat().bytesPerPixel == 2) {
+ uint16 color = READ_UINT16(maskSrc);
+ if (!isTransparent(color))
+ memcpy(dst, src, 2);
+ } else if (g_system->getScreenFormat().bytesPerPixel == 4) {
+ uint32 color = READ_UINT32(maskSrc);
+ if (!isTransparent(color))
+ memcpy(dst, src, 4);
+ }
+
+ src += g_system->getScreenFormat().bytesPerPixel;
+ maskSrc += g_system->getScreenFormat().bytesPerPixel;
+ dst += g_system->getScreenFormat().bytesPerPixel;
+ }
+
+ src += _surface->pitch - lineSize;
+ dst += screen->pitch - lineSize;
+ }
+}
+
+void Surface::copyToCurrentPortTransparentGlow(const Common::Rect &srcRect, const Common::Rect &dstRect) const {
+ // This is the same as copyToCurrentPortTransparent(), but turns the red value of each
+ // pixel all the way up.
+
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface();
+ byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top);
+ byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top);
+
+ int lineSize = srcRect.width() * _surface->format.bytesPerPixel;
+
+ for (int y = 0; y < srcRect.height(); y++) {
+ for (int x = 0; x < srcRect.width(); x++) {
+ if (g_system->getScreenFormat().bytesPerPixel == 2) {
+ uint16 color = READ_UINT16(src);
+ if (!isTransparent(color))
+ WRITE_UINT16(dst, getGlowColor(color));
+ } else if (g_system->getScreenFormat().bytesPerPixel == 4) {
+ uint32 color = READ_UINT32(src);
+ if (!isTransparent(color))
+ WRITE_UINT32(dst, getGlowColor(color));
+ }
+
+ src += g_system->getScreenFormat().bytesPerPixel;
+ dst += g_system->getScreenFormat().bytesPerPixel;
+ }
+
+ src += _surface->pitch - lineSize;
+ dst += screen->pitch - lineSize;
+ }
+}
+
+void Surface::scaleTransparentCopy(const Common::Rect &srcRect, const Common::Rect &dstRect) const {
+ // I'm doing simple linear scaling here
+ // dstRect(x, y) = srcRect(x * srcW / dstW, y * srcH / dstH);
+
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface();
+
+ int srcW = srcRect.width();
+ int srcH = srcRect.height();
+ int dstW = dstRect.width();
+ int dstH = dstRect.height();
+
+ for (int y = 0; y < dstH; y++) {
+ for (int x = 0; x < dstW; x++) {
+ if (g_system->getScreenFormat().bytesPerPixel == 2) {
+ uint16 color = READ_UINT16((byte *)_surface->getBasePtr(
+ x * srcW / dstW + srcRect.left,
+ y * srcH / dstH + srcRect.top));
+ if (!isTransparent(color))
+ WRITE_UINT16((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), color);
+ } else if (g_system->getScreenFormat().bytesPerPixel == 4) {
+ uint32 color = READ_UINT32((byte *)_surface->getBasePtr(
+ x * srcW / dstW + srcRect.left,
+ y * srcH / dstH + srcRect.top));
+ if (!isTransparent(color))
+ WRITE_UINT32((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), color);
+ }
+ }
+ }
+}
+
+void Surface::scaleTransparentCopyGlow(const Common::Rect &srcRect, const Common::Rect &dstRect) const {
+ // This is the same as scaleTransparentCopy(), but turns the red value of each
+ // pixel all the way up.
+
+ Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface();
+
+ int srcW = srcRect.width();
+ int srcH = srcRect.height();
+ int dstW = dstRect.width();
+ int dstH = dstRect.height();
+
+ for (int y = 0; y < dstH; y++) {
+ for (int x = 0; x < dstW; x++) {
+ if (g_system->getScreenFormat().bytesPerPixel == 2) {
+ uint16 color = READ_UINT16((byte *)_surface->getBasePtr(
+ x * srcW / dstW + srcRect.left,
+ y * srcH / dstH + srcRect.top));
+ if (!isTransparent(color))
+ WRITE_UINT16((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), getGlowColor(color));
+ } else if (g_system->getScreenFormat().bytesPerPixel == 4) {
+ uint32 color = READ_UINT32((byte *)_surface->getBasePtr(
+ x * srcW / dstW + srcRect.left,
+ y * srcH / dstH + srcRect.top));
+ if (!isTransparent(color))
+ WRITE_UINT32((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), getGlowColor(color));
+ }
+ }
+ }
+}
+
+uint32 Surface::getGlowColor(uint32 color) const {
+ // Can't just 'or' it on like the original did :P
+ byte r, g, b;
+ g_system->getScreenFormat().colorToRGB(color, r, g, b);
+ return g_system->getScreenFormat().RGBToColor(0xff, g, b);
+}
+
+bool Surface::isTransparent(uint32 color) const {
+ // HACK: Seems we're truncating some color data somewhere...
+ uint32 transColor1 = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff);
+ uint32 transColor2 = g_system->getScreenFormat().RGBToColor(0xf8, 0xf8, 0xf8);
+
+ return color == transColor1 || color == transColor2;
+}
+
+PixelImage::PixelImage() {
+ _transparent = false;
+}
+
+void PixelImage::drawImage(const Common::Rect &sourceBounds, const Common::Rect &destBounds) {
+ if (!isSurfaceValid())
+ return;
+
+ // Draw from sourceBounds to destBounds based on _transparent
+ if (_transparent)
+ copyToCurrentPortTransparent(sourceBounds, destBounds);
+ else
+ copyToCurrentPort(sourceBounds, destBounds);
+}
+
+void Frame::initFromPICTFile(const Common::String &fileName, bool transparent) {
+ getImageFromPICTFile(fileName);
+ _transparent = transparent;
+}
+
+void Frame::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) {
+ getImageFromPICTResource(resFork, id);
+ _transparent = transparent;
+}
+
+void Frame::initFromMovieFrame(Video::VideoDecoder *video, TimeValue time, bool transparent) {
+ getImageFromMovieFrame(video, time);
+ _transparent = transparent;
+}
+
+void Picture::draw(const Common::Rect &r) {
+ Common::Rect surfaceBounds;
+ getSurfaceBounds(surfaceBounds);
+ Common::Rect r1 = r;
+
+ Common::Rect bounds;
+ getBounds(bounds);
+ surfaceBounds.moveTo(bounds.left, bounds.top);
+ r1 = r1.findIntersectingRect(surfaceBounds);
+ getSurfaceBounds(surfaceBounds);
+
+ Common::Rect r2 = r1;
+ r2.translate(surfaceBounds.left - bounds.left, surfaceBounds.top - bounds.top);
+ drawImage(r2, r1);
+}
+
+void Picture::initFromPICTFile(const Common::String &fileName, bool transparent) {
+ Frame::initFromPICTFile(fileName, transparent);
+
+ Common::Rect surfaceBounds;
+ getSurfaceBounds(surfaceBounds);
+ sizeElement(surfaceBounds.width(), surfaceBounds.height());
+}
+
+void Picture::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) {
+ Frame::initFromPICTResource(resFork, id, transparent);
+
+ Common::Rect surfaceBounds;
+ getSurfaceBounds(surfaceBounds);
+ sizeElement(surfaceBounds.width(), surfaceBounds.height());
+}
+
+void Picture::initFromMovieFrame(Video::VideoDecoder *video, TimeValue time, bool transparent) {
+ Frame::initFromMovieFrame(video, time, transparent);
+
+ Common::Rect surfaceBounds;
+ getSurfaceBounds(surfaceBounds);
+ sizeElement(surfaceBounds.width(), surfaceBounds.height());
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/surface.h b/engines/pegasus/surface.h
new file mode 100644
index 0000000000..4588146fb4
--- /dev/null
+++ b/engines/pegasus/surface.h
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_SURFACE_H
+#define PEGASUS_SURFACE_H
+
+#include "common/rect.h"
+#include "common/str.h"
+
+#include "pegasus/elements.h"
+#include "pegasus/types.h"
+
+namespace Common {
+ class MacResManager;
+}
+
+namespace Graphics {
+ struct Surface;
+}
+
+namespace Video {
+ class VideoDecoder;
+}
+
+namespace Pegasus {
+
+// Surface bounds are always normalized.
+
+class Surface {
+public:
+ Surface();
+ virtual ~Surface();
+
+ virtual void allocateSurface(const Common::Rect &);
+ virtual void deallocateSurface();
+ virtual void shareSurface(Surface *surface);
+ bool isSurfaceValid() const { return _surface != 0; }
+
+ Graphics::Surface *getSurface() const { return _surface; }
+ void getSurfaceBounds(Common::Rect &r) { r = _bounds; }
+
+ // None of the copyToCurrentPort* functions do any sanity checks.
+ // It's up to clients to make sure that the Surface is valid.
+ void copyToCurrentPort() const;
+ void copyToCurrentPortTransparent() const;
+ void copyToCurrentPort(const Common::Rect &) const;
+ void copyToCurrentPortTransparent(const Common::Rect &) const;
+ void copyToCurrentPort(const Common::Rect &, const Common::Rect &) const;
+ void copyToCurrentPortTransparent(const Common::Rect &, const Common::Rect &) const;
+ void copyToCurrentPortMasked(const Common::Rect &, const Common::Rect &, const Surface *) const;
+ void copyToCurrentPortTransparentGlow(const Common::Rect &, const Common::Rect &) const;
+ void scaleTransparentCopy(const Common::Rect &, const Common::Rect &) const;
+ void scaleTransparentCopyGlow(const Common::Rect &, const Common::Rect &) const;
+
+ virtual void getImageFromPICTFile(const Common::String &fileName);
+ virtual void getImageFromPICTResource(Common::MacResManager *resFork, uint16 id);
+ virtual void getImageFromMovieFrame(Video::VideoDecoder *, TimeValue);
+
+protected:
+ bool _ownsSurface;
+ Graphics::Surface *_surface;
+ Common::Rect _bounds;
+
+private:
+ void getImageFromPICTStream(Common::SeekableReadStream *stream);
+
+ uint32 getGlowColor(uint32 color) const;
+ bool isTransparent(uint32 color) const;
+};
+
+class PixelImage : public Surface {
+public:
+ PixelImage();
+ virtual ~PixelImage() {}
+
+ void drawImage(const Common::Rect &, const Common::Rect &);
+
+protected:
+ virtual void setTransparent(bool transparent) { _transparent = transparent; }
+
+ bool _transparent;
+};
+
+class Frame : public PixelImage {
+public:
+ Frame() {}
+ virtual ~Frame() {}
+
+ virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false);
+ virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false);
+ virtual void initFromMovieFrame(Video::VideoDecoder *, TimeValue, bool transparent = false);
+};
+
+class SpriteFrame : public Frame {
+friend class Sprite;
+public:
+ SpriteFrame() { _referenceCount = 0; }
+ virtual ~SpriteFrame() {}
+
+protected:
+ uint32 _referenceCount;
+};
+
+class Picture : public DisplayElement, public Frame {
+public:
+ Picture(const DisplayElementID id) : DisplayElement(id) {}
+ virtual ~Picture() {}
+
+ virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false);
+ virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false);
+ virtual void initFromMovieFrame(Video::VideoDecoder *, TimeValue, bool transparent = false);
+
+ virtual void draw(const Common::Rect &);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/timers.cpp b/engines/pegasus/timers.cpp
new file mode 100644
index 0000000000..3b875038cc
--- /dev/null
+++ b/engines/pegasus/timers.cpp
@@ -0,0 +1,429 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/notification.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+Idler::Idler() {
+ _isIdling = false;
+ _nextIdler = 0;
+ _prevIdler = 0;
+}
+
+Idler::~Idler() {
+ stopIdling();
+}
+
+void Idler::startIdling() {
+ if (!isIdling()) {
+ ((PegasusEngine *)g_engine)->addIdler(this);
+ _isIdling = true;
+ }
+}
+
+void Idler::stopIdling() {
+ if (isIdling()) {
+ ((PegasusEngine *)g_engine)->removeIdler(this);
+ _isIdling = false;
+ }
+}
+
+TimeBase::TimeBase(const TimeScale preferredScale) {
+ _preferredScale = preferredScale;
+ _callBackList = 0;
+ _paused = false;
+ _flags = 0;
+ _lastMillis = 0;
+ _time = 0;
+ _rate = 0;
+ _startTime = 0;
+ _startScale = 1;
+ _stopTime = 0xffffffff;
+ _stopScale = 1;
+ _master = 0;
+ _pausedRate = 0;
+ _pauseStart = 0;
+
+ ((PegasusEngine *)g_engine)->addTimeBase(this);
+}
+
+TimeBase::~TimeBase() {
+ if (_master)
+ _master->_slaves.remove(this);
+
+ ((PegasusEngine *)g_engine)->removeTimeBase(this);
+ disposeAllCallBacks();
+
+ // TODO: Remove slaves? Make them remove themselves?
+}
+
+void TimeBase::setTime(const TimeValue time, const TimeScale scale) {
+ _time = Common::Rational(time, (scale == 0) ? _preferredScale : scale);
+ _lastMillis = 0;
+}
+
+TimeValue TimeBase::getTime(const TimeScale scale) {
+ return _time.getNumerator() * ((scale == 0) ? _preferredScale : scale) / _time.getDenominator();
+}
+
+void TimeBase::setRate(const Common::Rational rate) {
+ _rate = rate;
+
+ if (_rate == 0)
+ _paused = false;
+}
+
+Common::Rational TimeBase::getEffectiveRate() const {
+ return _rate * ((_master == 0) ? 1 : _master->getEffectiveRate());
+}
+
+void TimeBase::start() {
+ if (_paused)
+ _pausedRate = 1;
+ else
+ setRate(1);
+}
+
+void TimeBase::stop() {
+ setRate(0);
+ _paused = false;
+}
+
+void TimeBase::pause() {
+ if (isRunning() && !_paused) {
+ _pausedRate = getRate();
+ stop();
+ _paused = true;
+ _pauseStart = g_system->getMillis();
+ }
+}
+
+void TimeBase::resume() {
+ if (_paused) {
+ setRate(_pausedRate);
+ _paused = false;
+
+ if (isRunning())
+ _lastMillis += g_system->getMillis() - _pauseStart;
+ }
+}
+
+bool TimeBase::isRunning() {
+ if (_paused && _pausedRate != 0)
+ return true;
+
+ Common::Rational rate = getRate();
+
+ if (rate == 0)
+ return false;
+
+ if (getFlags() & kLoopTimeBase)
+ return true;
+
+ if (rate > 0)
+ return getTime() != getStop();
+
+ return getTime() != getStart();
+}
+
+void TimeBase::setStart(const TimeValue startTime, const TimeScale scale) {
+ _startTime = startTime;
+ _startScale = (scale == 0) ? _preferredScale : scale;
+}
+
+TimeValue TimeBase::getStart(const TimeScale scale) const {
+ if (scale)
+ return _startTime * scale / _startScale;
+
+ return _startTime * _preferredScale / _startScale;
+}
+
+void TimeBase::setStop(const TimeValue stopTime, const TimeScale scale) {
+ _stopTime = stopTime;
+ _stopScale = (scale == 0) ? _preferredScale : scale;
+}
+
+TimeValue TimeBase::getStop(const TimeScale scale) const {
+ if (scale)
+ return _stopTime * scale / _stopScale;
+
+ return _stopTime * _preferredScale / _stopScale;
+}
+
+void TimeBase::setSegment(const TimeValue startTime, const TimeValue stopTime, const TimeScale scale) {
+ setStart(startTime, scale);
+ setStop(stopTime, scale);
+}
+
+void TimeBase::getSegment(TimeValue &startTime, TimeValue &stopTime, const TimeScale scale) const {
+ startTime = getStart(scale);
+ stopTime = getStop(scale);
+}
+
+TimeValue TimeBase::getDuration(const TimeScale scale) const {
+ TimeValue startTime, stopTime;
+ getSegment(startTime, stopTime, scale);
+ return stopTime - startTime;
+}
+
+void TimeBase::setMasterTimeBase(TimeBase *tb) {
+ // TODO: We're just ignoring the master (except for effective rate)
+ // for now to simplify things
+ if (_master)
+ _master->_slaves.remove(this);
+
+ _master = tb;
+
+ if (_master)
+ _master->_slaves.push_back(this);
+}
+
+void TimeBase::updateTime() {
+ if (_lastMillis == 0) {
+ _lastMillis = g_system->getMillis();
+ } else {
+ uint32 curTime = g_system->getMillis();
+ if (_lastMillis == curTime) // No change
+ return;
+
+ _time += Common::Rational(curTime - _lastMillis, 1000) * getEffectiveRate();
+ _lastMillis = curTime;
+ }
+}
+
+void TimeBase::checkCallBacks() {
+ // Nothing to do if we're paused or not running
+ if (_paused || !isRunning())
+ return;
+
+ Common::Rational startTime = Common::Rational(_startTime, _startScale);
+ Common::Rational stopTime = Common::Rational(_stopTime, _stopScale);
+
+ // First step: update the times
+ updateTime();
+
+ // Clip time to the boundaries
+ if (_time >= stopTime)
+ _time = stopTime;
+ else if (_time <= startTime)
+ _time = startTime;
+
+ // TODO: Update the slaves?
+
+ Common::Rational time = Common::Rational(getTime(), getScale());
+
+ // Check if we've triggered any callbacks
+ for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = runner->_nextCallBack) {
+ if (runner->_hasBeenTriggered)
+ continue;
+
+ if (runner->_type == kCallBackAtTime && runner->_trigger == kTriggerTimeFwd) {
+ if (getTime() >= (runner->_param2 * _preferredScale / runner->_param3) && getRate() > 0) {
+ uint param2 = runner->_param2, param3 = runner->_param3;
+ runner->callBack();
+ // HACK: Only stop future time forward callbacks if the parameters have not been changed
+ // This fixes striding callbacks. Since only striding callbacks do this kind of
+ // craziness, I'm not too worried about this.
+ runner->_hasBeenTriggered = (runner->_param2 == param2 && runner->_param3 == param3);
+ }
+ } else if (runner->_type == kCallBackAtExtremes) {
+ if (runner->_trigger == kTriggerAtStop) {
+ if (time == stopTime) {
+ runner->callBack();
+ runner->_hasBeenTriggered = true;
+ }
+ } else if (runner->_trigger == kTriggerAtStart) {
+ if (time == startTime) {
+ runner->callBack();
+ runner->_hasBeenTriggered = true;
+ }
+ }
+ }
+ }
+
+ if (getFlags() & kLoopTimeBase) {
+ // Loop if necessary
+ if (getRate() < 0 && time == startTime)
+ setTime(_stopTime, _stopScale);
+ else if (getRate() > 0 && time == stopTime)
+ setTime(_startTime, _startScale);
+ } else {
+ // Stop at the end
+ if ((getRate() > 0 && time == stopTime) || (getRate() < 0 && time == startTime))
+ stop();
+ }
+}
+
+// Protected functions only called by TimeBaseCallBack.
+
+void TimeBase::addCallBack(TimeBaseCallBack *callBack) {
+ callBack->_nextCallBack = _callBackList;
+ _callBackList = callBack;
+}
+
+void TimeBase::removeCallBack(TimeBaseCallBack *callBack) {
+ if (_callBackList == callBack) {
+ _callBackList = callBack->_nextCallBack;
+ } else {
+ TimeBaseCallBack *runner, *prevRunner;
+
+ for (runner = _callBackList->_nextCallBack, prevRunner = _callBackList; runner != callBack; prevRunner = runner, runner = runner->_nextCallBack)
+ ;
+
+ prevRunner->_nextCallBack = runner->_nextCallBack;
+ }
+
+ callBack->_nextCallBack = 0;
+}
+
+void TimeBase::disposeAllCallBacks() {
+ TimeBaseCallBack *nextRunner;
+
+ for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = nextRunner) {
+ nextRunner = runner->_nextCallBack;
+ runner->disposeCallBack();
+ runner->_nextCallBack = 0;
+ }
+
+ _callBackList = 0;
+}
+
+TimeBaseCallBack::TimeBaseCallBack() {
+ _timeBase = 0;
+ _nextCallBack = 0;
+ _trigger = kTriggerNone;
+ _type = kCallBackNone;
+ _hasBeenTriggered = false;
+}
+
+TimeBaseCallBack::~TimeBaseCallBack() {
+ releaseCallBack();
+}
+
+void TimeBaseCallBack::initCallBack(TimeBase *tb, CallBackType type) {
+ releaseCallBack();
+ _timeBase = tb;
+ _timeBase->addCallBack(this);
+ _type = type;
+}
+
+void TimeBaseCallBack::releaseCallBack() {
+ if (_timeBase)
+ _timeBase->removeCallBack(this);
+ disposeCallBack();
+}
+
+void TimeBaseCallBack::disposeCallBack() {
+ _timeBase = 0;
+ _hasBeenTriggered = false;
+}
+
+void TimeBaseCallBack::scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3) {
+ // TODO: Rename param2/param3?
+ _trigger = trigger;
+ _param2 = param2;
+ _param3 = param3;
+ _hasBeenTriggered = false;
+}
+
+void TimeBaseCallBack::cancelCallBack() {
+ _trigger = kTriggerNone;
+ _hasBeenTriggered = false;
+}
+
+IdlerTimeBase::IdlerTimeBase() {
+ _lastTime = 0xffffffff;
+ startIdling();
+}
+
+void IdlerTimeBase::useIdleTime() {
+ uint32 currentTime = getTime();
+ if (currentTime != _lastTime) {
+ _lastTime = currentTime;
+ timeChanged(_lastTime);
+ }
+}
+
+NotificationCallBack::NotificationCallBack() {
+ _callBackFlag = 0;
+ _notifier = 0;
+}
+
+void NotificationCallBack::callBack() {
+ if (_notifier)
+ _notifier->setNotificationFlags(_callBackFlag, _callBackFlag);
+}
+
+static const NotificationFlags kFuseExpiredFlag = 1;
+
+Fuse::Fuse() : _fuseNotification(0, (NotificationManager *)((PegasusEngine *)g_engine)) {
+ _fuseNotification.notifyMe(this, kFuseExpiredFlag, kFuseExpiredFlag);
+ _fuseCallBack.setNotification(&_fuseNotification);
+ _fuseCallBack.initCallBack(&_fuseTimer, kCallBackAtExtremes);
+ _fuseCallBack.setCallBackFlag(kFuseExpiredFlag);
+}
+
+void Fuse::primeFuse(const TimeValue frequency, const TimeScale scale) {
+ stopFuse();
+ _fuseTimer.setScale(scale);
+ _fuseTimer.setSegment(0, frequency);
+ _fuseTimer.setTime(0);
+}
+
+void Fuse::lightFuse() {
+ if (!_fuseTimer.isRunning()) {
+ _fuseCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _fuseTimer.start();
+ }
+}
+
+void Fuse::stopFuse() {
+ _fuseTimer.stop();
+ _fuseCallBack.cancelCallBack();
+ // Make sure the fuse has not triggered but not been caught yet...
+ _fuseNotification.setNotificationFlags(0, 0xffffffff);
+}
+
+void Fuse::advanceFuse(const TimeValue time) {
+ if (_fuseTimer.isRunning()) {
+ _fuseTimer.stop();
+ _fuseTimer.setTime(_fuseTimer.getTime() + time);
+ _fuseTimer.start();
+ }
+}
+
+TimeValue Fuse::getTimeRemaining() {
+ return _fuseTimer.getStop() - _fuseTimer.getTime();
+}
+
+void Fuse::receiveNotification(Notification *, const NotificationFlags) {
+ stopFuse();
+ invokeAction();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/timers.h b/engines/pegasus/timers.h
new file mode 100644
index 0000000000..bcdca6e860
--- /dev/null
+++ b/engines/pegasus/timers.h
@@ -0,0 +1,260 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_TIMERS_H
+#define PEGASUS_TIMERS_H
+
+#include "common/list.h"
+#include "common/rational.h"
+#include "common/func.h"
+
+#include "pegasus/constants.h"
+#include "pegasus/notification.h"
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+class Idler {
+friend class PegasusEngine;
+
+public:
+ Idler();
+ virtual ~Idler();
+
+ virtual void startIdling();
+ virtual void stopIdling();
+ bool isIdling() const { return _isIdling; }
+
+protected:
+ virtual void useIdleTime() {}
+
+ bool _isIdling;
+ Idler *_nextIdler, *_prevIdler;
+};
+
+enum {
+ kLoopTimeBase = 1,
+ kPalindromeLoopTimeBase = 2,
+ kMaintainTimeBaseZero = 4
+};
+
+class TimeBaseCallBack;
+
+class TimeBase {
+friend class TimeBaseCallBack;
+public:
+ TimeBase(const TimeScale = kDefaultTimeScale);
+ virtual ~TimeBase();
+
+ virtual void setTime(const TimeValue, const TimeScale = 0);
+ virtual TimeValue getTime(const TimeScale = 0);
+
+ virtual void setScale(const TimeScale scale) { _preferredScale = scale; }
+ virtual TimeScale getScale() const { return _preferredScale; }
+
+ virtual void setRate(const Common::Rational);
+ virtual Common::Rational getRate() const { return _rate; }
+
+ virtual void start();
+ virtual void stop();
+ virtual bool isRunning();
+
+ virtual void pause();
+ virtual void resume();
+ bool isPaused() const { return _paused; }
+
+ virtual void setFlags(const uint32 flags) { _flags = flags; }
+ virtual uint32 getFlags() const { return _flags; }
+
+ virtual void setStart(const TimeValue, const TimeScale = 0);
+ virtual TimeValue getStart(const TimeScale = 0) const;
+
+ virtual void setStop(const TimeValue, const TimeScale = 0);
+ virtual TimeValue getStop(const TimeScale = 0) const;
+
+ virtual void setSegment(const TimeValue, const TimeValue, const TimeScale = 0);
+ virtual void getSegment(TimeValue&, TimeValue&, const TimeScale = 0) const;
+
+ virtual TimeValue getDuration(const TimeScale = 0) const;
+
+ virtual void setMasterTimeBase(TimeBase *timeBase);
+
+ void disposeAllCallBacks();
+
+ // ScummVM's API additions (to replace the need for actual timers)
+ virtual void checkCallBacks();
+
+protected:
+ void addCallBack(TimeBaseCallBack *);
+ void removeCallBack(TimeBaseCallBack *);
+ virtual void updateTime();
+
+ TimeBase *_master;
+ TimeScale _preferredScale;
+ TimeBaseCallBack *_callBackList;
+ Common::Rational _rate, _pausedRate;
+ bool _paused;
+ uint32 _startTime, _startScale;
+ uint32 _stopTime, _stopScale;
+ uint32 _flags;
+
+ Common::Rational _time;
+ uint32 _lastMillis, _pauseStart;
+
+private:
+ Common::Rational getEffectiveRate() const;
+
+ Common::List<TimeBase *> _slaves;
+};
+
+// Type passed to initCallBack()
+enum CallBackType {
+ kCallBackNone = 0,
+ kCallBackAtTime = 1,
+ kCallBackAtExtremes = 4
+};
+
+// Trigger passed to scheduleCallBack()
+enum CallBackTrigger {
+ kTriggerNone = 0,
+
+ // AtTime flags
+ kTriggerTimeFwd = 1,
+
+ // AtExtremes flags
+ kTriggerAtStart = 1,
+ kTriggerAtStop = 2
+};
+
+class TimeBaseCallBack {
+friend class TimeBase;
+
+public:
+ TimeBaseCallBack();
+ virtual ~TimeBaseCallBack();
+
+ void initCallBack(TimeBase *, CallBackType type);
+
+ void releaseCallBack();
+
+ void scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3);
+ void cancelCallBack();
+
+protected:
+ virtual void callBack() = 0;
+
+ TimeBase *_timeBase;
+
+ // Owned and operated by TimeBase;
+ TimeBaseCallBack *_nextCallBack;
+
+ // Our storage of the QuickTime timer crap
+ CallBackType _type;
+ CallBackTrigger _trigger;
+ uint32 _param2, _param3;
+ bool _hasBeenTriggered;
+
+private:
+ void disposeCallBack();
+};
+
+class IdlerTimeBase : public Idler, public TimeBase {
+public:
+ IdlerTimeBase();
+ virtual ~IdlerTimeBase() { stopIdling(); }
+
+ TimeValue getLastTime() const { return _lastTime; }
+
+protected:
+ virtual void useIdleTime();
+ virtual void timeChanged(const TimeValue) {}
+
+ TimeValue _lastTime;
+
+};
+
+class NotificationCallBack : public TimeBaseCallBack {
+public:
+ NotificationCallBack();
+ virtual ~NotificationCallBack() {}
+
+ void setNotification(Notification *notifier) { _notifier = notifier; }
+
+ void setCallBackFlag(const NotificationFlags flag) { _callBackFlag = flag; }
+ NotificationFlags getCallBackFlag() const { return _callBackFlag; }
+
+protected:
+ void callBack();
+
+ Notification *_notifier;
+ NotificationFlags _callBackFlag;
+};
+
+class DynamicElement : public TimeBase {
+public:
+ TimeValue percentSeconds(const uint32 percent) { return getScale() * percent / 100; }
+};
+
+class Fuse : private NotificationReceiver {
+public:
+ Fuse();
+ virtual ~Fuse() {}
+
+ void primeFuse(const TimeValue, const TimeScale = 1); // An appropriately named function :P
+ void lightFuse();
+ void stopFuse();
+ bool isFuseLit() { return _fuseTimer.isRunning() || _fuseTimer.isPaused(); }
+ void advanceFuse(const TimeValue);
+ TimeValue getTimeRemaining();
+ TimeScale getFuseScale() { return _fuseTimer.getScale(); }
+
+ void pauseFuse() { _fuseTimer.pause(); }
+ void resumeFuse() { _fuseTimer.resume(); }
+ bool isFusePaused() { return _fuseTimer.isPaused(); }
+
+protected:
+ virtual void receiveNotification(Notification *, const NotificationFlags);
+ virtual void invokeAction() {}
+
+ TimeBase _fuseTimer;
+ NotificationCallBack _fuseCallBack;
+ Notification _fuseNotification;
+};
+
+class FuseFunction : public Fuse {
+public:
+ FuseFunction() : _functor(0) {}
+ virtual ~FuseFunction() { delete _functor; }
+
+ void setFunctor(Common::Functor0<void> *functor) { delete _functor; _functor = functor; }
+protected:
+ virtual void invokeAction() { if (_functor && _functor->isValid()) (*_functor)(); }
+
+ Common::Functor0<void> *_functor;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/transition.cpp b/engines/pegasus/transition.cpp
new file mode 100644
index 0000000000..1ae212df85
--- /dev/null
+++ b/engines/pegasus/transition.cpp
@@ -0,0 +1,200 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "graphics/surface.h"
+
+#include "pegasus/transition.h"
+
+namespace Pegasus {
+
+ScreenFader::ScreenFader() {
+ _isBlack = true;
+ // Initially, assume screens are on at full brightness.
+ Fader::setFaderValue(100);
+ _screen = new Graphics::Surface();
+}
+
+ScreenFader::~ScreenFader() {
+ _screen->free();
+ delete _screen;
+}
+
+void ScreenFader::doFadeOutSync(const TimeValue duration, const TimeValue scale, bool isBlack) {
+ _isBlack = isBlack;
+ _screen->copyFrom(*g_system->lockScreen());
+ g_system->unlockScreen();
+
+ FaderMoveSpec spec;
+ spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 0);
+ startFaderSync(spec);
+
+ _screen->free();
+}
+
+void ScreenFader::doFadeInSync(const TimeValue duration, const TimeValue scale, bool isBlack) {
+ _isBlack = isBlack;
+ _screen->copyFrom(*g_system->lockScreen());
+ g_system->unlockScreen();
+
+ FaderMoveSpec spec;
+ spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 100);
+ startFaderSync(spec);
+
+ _screen->free();
+}
+
+void ScreenFader::setFaderValue(const int32 value) {
+ if (value != getFaderValue()) {
+ Fader::setFaderValue(value);
+
+ if (_screen->pixels) {
+ // The original game does a gamma fade here using the Mac API. In order to do
+ // that, it would require an immense amount of CPU processing. This does a
+ // linear fade instead, which looks fairly well, IMO.
+ Graphics::Surface *screen = g_system->lockScreen();
+
+ for (uint y = 0; y < _screen->h; y++) {
+ for (uint x = 0; x < _screen->w; x++) {
+ if (_screen->format.bytesPerPixel == 2)
+ WRITE_UINT16(screen->getBasePtr(x, y), fadePixel(READ_UINT16(_screen->getBasePtr(x, y)), value));
+ else
+ WRITE_UINT32(screen->getBasePtr(x, y), fadePixel(READ_UINT32(_screen->getBasePtr(x, y)), value));
+ }
+ }
+
+ g_system->unlockScreen();
+ g_system->updateScreen();
+ }
+ }
+}
+
+static inline byte fadeComponent(byte comp, int32 percent) {
+ return comp * percent / 100;
+}
+
+uint32 ScreenFader::fadePixel(uint32 color, int32 percent) const {
+ byte r, g, b;
+ g_system->getScreenFormat().colorToRGB(color, r, g, b);
+
+ if (_isBlack) {
+ r = fadeComponent(r, percent);
+ g = fadeComponent(g, percent);
+ b = fadeComponent(b, percent);
+ } else {
+ r = 0xFF - fadeComponent(0xFF - r, percent);
+ g = 0xFF - fadeComponent(0xFF - g, percent);
+ b = 0xFF - fadeComponent(0xFF - b, percent);
+ }
+
+ return g_system->getScreenFormat().RGBToColor(r, g, b);
+}
+
+Transition::Transition(const DisplayElementID id) : FaderAnimation(id) {
+ _outPicture = 0;
+ _inPicture = 0;
+}
+
+void Transition::setBounds(const Common::Rect &r) {
+ FaderAnimation::setBounds(r);
+ _boundsWidth = _bounds.width();
+ _boundsHeight = _bounds.height();
+}
+
+void Transition::setInAndOutElements(DisplayElement *inElement, DisplayElement *outElement) {
+ _inPicture = inElement;
+ _outPicture = outElement;
+
+ Common::Rect r;
+
+ if (_outPicture)
+ _outPicture->getBounds(r);
+ else if (_inPicture)
+ _inPicture->getBounds(r);
+
+ setBounds(r);
+}
+
+void Slide::draw(const Common::Rect &r) {
+ Common::Rect oldBounds, newBounds;
+
+ adjustSlideRects(oldBounds, newBounds);
+ drawElements(r, oldBounds, newBounds);
+}
+
+void Slide::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) {
+ oldBounds = _bounds;
+ newBounds = _bounds;
+}
+
+void Slide::drawElements(const Common::Rect &drawRect, const Common::Rect &oldBounds, const Common::Rect &newBounds) {
+ drawSlideElement(drawRect, oldBounds, _outPicture);
+ drawSlideElement(drawRect, newBounds, _inPicture);
+}
+
+void Slide::drawSlideElement(const Common::Rect &drawRect, const Common::Rect &oldBounds, DisplayElement *picture) {
+ if (picture && drawRect.intersects(oldBounds)) {
+ picture->moveElementTo(oldBounds.left, oldBounds.top);
+ picture->draw(drawRect.findIntersectingRect(oldBounds));
+ }
+}
+
+void Push::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) {
+ switch (_direction & kSlideHorizMask) {
+ case kSlideLeftMask:
+ newBounds.left = oldBounds.right = _bounds.right - pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange);
+ newBounds.right = newBounds.left + _boundsWidth;
+ oldBounds.left = oldBounds.right - _boundsWidth;
+ break;
+ case kSlideRightMask:
+ oldBounds.left = newBounds.right = _bounds.left + pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange);
+ oldBounds.right = oldBounds.left + _boundsWidth;
+ newBounds.left = newBounds.right - _boundsWidth;
+ break;
+ default:
+ newBounds.left = oldBounds.left = _bounds.left;
+ newBounds.right = oldBounds.right = _bounds.right;
+ break;
+ }
+
+ switch (_direction & kSlideVertMask) {
+ case kSlideDownMask:
+ oldBounds.top = newBounds.bottom = _bounds.top + pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange);
+ oldBounds.bottom = oldBounds.top + _boundsHeight;
+ newBounds.top = newBounds.bottom - _boundsHeight;
+ break;
+ case kSlideUpMask:
+ newBounds.top = oldBounds.bottom = _bounds.bottom - pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange);
+ newBounds.bottom = newBounds.top + _boundsHeight;
+ oldBounds.top = oldBounds.bottom - _boundsHeight;
+ break;
+ default:
+ newBounds.top = oldBounds.top = _bounds.top;
+ newBounds.bottom = oldBounds.bottom = _bounds.bottom;
+ break;
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/transition.h b/engines/pegasus/transition.h
new file mode 100644
index 0000000000..84241a2bd2
--- /dev/null
+++ b/engines/pegasus/transition.h
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_TRANSITION_H
+#define PEGASUS_TRANSITION_H
+
+#include "pegasus/fader.h"
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace Pegasus {
+
+class ScreenFader : public Fader {
+public:
+ ScreenFader();
+ virtual ~ScreenFader();
+
+ void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true);
+ void doFadeInSync(const TimeValue = kHalfSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true);
+
+ void setFaderValue(const int32);
+
+private:
+ bool _isBlack;
+ uint32 fadePixel(uint32 color, int32 percent) const;
+ Graphics::Surface *_screen;
+};
+
+// Transitions are faders that range over [0,1000], which makes their
+// "resolution" one tenth of a percent
+
+static const int kTransitionBottom = 0;
+static const int kTransitionTop = 1000;
+
+static const int kTransitionRange = kTransitionTop - kTransitionBottom;
+
+class Transition : public FaderAnimation {
+public:
+ Transition(const DisplayElementID id);
+ virtual ~Transition() {}
+
+ virtual void setBounds(const Common::Rect &);
+
+ virtual void setInAndOutElements(DisplayElement *, DisplayElement *);
+ DisplayElement *getInElement() { return _inPicture; }
+ DisplayElement *getOutElement() { return _outPicture; }
+
+protected:
+ DisplayElement *_outPicture;
+ DisplayElement *_inPicture;
+
+ CoordType _boundsWidth, _boundsHeight;
+};
+
+class Slide : public Transition {
+public:
+ Slide(const DisplayElementID id) : Transition(id) {}
+ virtual ~Slide() {}
+
+ virtual void setSlideDirection(SlideDirection dir) { _direction = dir; }
+ virtual void draw(const Common::Rect &);
+
+ virtual void setDirection(const SlideDirection dir) { _direction = dir; }
+
+protected:
+ virtual void adjustSlideRects(Common::Rect &, Common::Rect &);
+ virtual void drawElements(const Common::Rect &, const Common::Rect &, const Common::Rect &);
+ virtual void drawSlideElement(const Common::Rect &, const Common::Rect &, DisplayElement *);
+
+ SlideDirection _direction;
+};
+
+class Push : public Slide {
+public:
+ Push(const DisplayElementID id) : Slide(id) {}
+ virtual ~Push() {}
+
+protected:
+ virtual void adjustSlideRects(Common::Rect &, Common::Rect &);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/types.h b/engines/pegasus/types.h
new file mode 100644
index 0000000000..64ab4e5bb2
--- /dev/null
+++ b/engines/pegasus/types.h
@@ -0,0 +1,161 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_TYPES_H
+#define PEGASUS_TYPES_H
+
+#include "common/scummsys.h"
+
+namespace Pegasus {
+
+// TODO: Probably all of these don't really need to be typedef'd...
+
+typedef int32 DisplayElementID;
+typedef int32 DisplayOrder;
+
+typedef int16 HotSpotID;
+typedef uint32 HotSpotFlags;
+
+typedef byte ButtonState;
+typedef uint32 InputBits;
+
+typedef int32 NotificationID;
+typedef uint32 NotificationFlags;
+
+// Mac types.
+typedef int16 ResIDType;
+typedef int16 CoordType;
+
+enum SlideDirection {
+ kSlideLeftMask = 1,
+ kSlideRightMask = kSlideLeftMask << 1,
+ kSlideUpMask = kSlideRightMask << 1 << 1,
+ kSlideDownMask = kSlideUpMask << 1,
+
+ kSlideHorizMask = kSlideLeftMask | kSlideRightMask,
+ kSlideVertMask = kSlideUpMask | kSlideDownMask,
+
+ kSlideUpLeftMask = kSlideLeftMask | kSlideUpMask,
+ kSlideUpRightMask = kSlideRightMask | kSlideUpMask,
+ kSlideDownLeftMask = kSlideLeftMask | kSlideDownMask,
+ kSlideDownRightMask = kSlideRightMask | kSlideDownMask
+};
+
+// ScummVM QuickTime/QuickDraw replacement types
+typedef uint TimeValue;
+typedef uint TimeScale;
+
+typedef int16 GameID;
+
+typedef GameID ItemID;
+typedef GameID ActorID;
+typedef GameID RoomID;
+typedef GameID NeighborhoodID;
+typedef byte AlternateID;
+typedef int8 HotSpotActivationID;
+
+typedef int16 WeightType;
+
+typedef byte DirectionConstant;
+typedef byte TurnDirection;
+
+// Meant to be room in low 16 bits and direction in high 16 bits.
+typedef uint32 RoomViewID;
+
+#define MakeRoomView(room, direction) (((RoomViewID) (room)) | (((RoomViewID) (direction)) << 16))
+
+typedef uint32 ExtraID;
+
+typedef int16 GameMode;
+
+typedef int16 WeightType;
+
+typedef int16 ItemState;
+
+typedef int8 DeathReason;
+
+typedef int32 GameMenuCommand;
+
+typedef int32 GameScoreType;
+
+typedef long CanMoveForwardReason;
+
+typedef long CanTurnReason;
+
+typedef long CanOpenDoorReason;
+
+enum InventoryResult {
+ kInventoryOK,
+ kTooMuchWeight,
+ kItemNotInInventory
+};
+
+typedef int32 InteractionID;
+
+typedef int32 AIConditionID;
+
+enum EnergyStage {
+ kStageNoStage,
+ kStageCasual, // more than 50% energy
+ kStageWorried, // more than 25% energy
+ kStageNervous, // more than 5% energy
+ kStagePanicStricken // less than 5% energy
+};
+
+enum NoradSubPrepState {
+ kSubNotPrepped,
+ kSubPrepped,
+ kSubDamaged
+};
+
+enum LowerClientSignature {
+ kNoClientSignature,
+ kInventorySignature,
+ kBiochipSignature,
+ kAISignature
+};
+
+enum LowerAreaSignature {
+ kLeftAreaSignature,
+ kMiddleAreaSignature,
+ kRightAreaSignature
+};
+
+enum AirQuality {
+ kAirQualityGood,
+ kAirQualityDirty,
+ kAirQualityVacuum
+};
+
+enum DragType {
+ kDragNoDrag,
+ kDragInventoryPickup,
+ kDragBiochipPickup,
+ kDragInventoryUse
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/util.cpp b/engines/pegasus/util.cpp
new file mode 100644
index 0000000000..59df610c33
--- /dev/null
+++ b/engines/pegasus/util.cpp
@@ -0,0 +1,77 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/random.h"
+#include "common/system.h"
+#include "common/util.h"
+
+#include "pegasus/util.h"
+
+namespace Pegasus {
+
+IDObject::IDObject(const int32 id) {
+ _objectID = id;
+}
+
+IDObject::~IDObject() {
+}
+
+int32 IDObject::getObjectID() const {
+ return _objectID;
+}
+
+int operator==(const IDObject &arg1, const IDObject &arg2) {
+ return arg1.getObjectID() == arg2.getObjectID();
+}
+
+int operator!=(const IDObject &arg1, const IDObject &arg2) {
+ return arg1.getObjectID() != arg2.getObjectID();
+}
+
+int32 pegasusRound(const int32 a, const int32 b) {
+ if (b < 0)
+ if (a < 0)
+ return -((a - (-b >> 1)) / -b);
+ else
+ return -((a + (-b >> 1)) / -b);
+ else
+ if (a < 0)
+ return (a - (b >> 1)) / b;
+ else
+ return (a + (b >> 1)) / b;
+}
+
+int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2) {
+ if (start2 == stop2)
+ return start2;
+ else
+ return start2 + pegasusRound((current1 - start1) * (stop2 - start2), (stop1 - start1));
+}
+
+uint32 tickCount() {
+ return g_system->getMillis() * 60 / 1000;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/util.h b/engines/pegasus/util.h
new file mode 100644
index 0000000000..97ba1c20c3
--- /dev/null
+++ b/engines/pegasus/util.h
@@ -0,0 +1,117 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_UTIL_H
+#define PEGASUS_UTIL_H
+
+#include "common/stream.h"
+
+#include "pegasus/types.h"
+
+namespace Common {
+ class RandomSource;
+}
+
+namespace Pegasus {
+
+class IDObject {
+public:
+ IDObject(const int32 id);
+ ~IDObject();
+
+ int32 getObjectID() const;
+
+private:
+ int32 _objectID;
+};
+
+#define NUM_FLAGS (sizeof(Unit) * 8)
+#define BIT_INDEX_SHIFT (sizeof(Unit) + 2 - (sizeof(Unit)) / 3)
+#define BIT_INDEX_MASK (NUM_FLAGS - 1)
+
+template <typename Unit, uint32 kNumFlags>
+class FlagsArray {
+public:
+ FlagsArray() { clearAllFlags(); }
+ void clearAllFlags() { memset(_flags, 0, sizeof(_flags)); }
+ void setAllFlags() { memset(_flags, ~((Unit)0), sizeof(_flags)); }
+ void setFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] |= 1 << (flag & BIT_INDEX_MASK); }
+ void clearFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] &= ~(1 << (flag & BIT_INDEX_MASK)); }
+ void setFlag(uint32 flag, bool val) { if (val) setFlag(flag); else clearFlag(flag); }
+ bool getFlag(uint32 flag) { return (_flags[flag >> BIT_INDEX_SHIFT] & (1 << (flag & BIT_INDEX_MASK))) != 0; }
+ bool anyFlagSet() {
+ for (uint32 i = 0; i < sizeof(_flags); i++)
+ if (_flags[i] != 0)
+ return true;
+ return false;
+ }
+
+ void readFromStream(Common::ReadStream *stream) {
+ // Shortcut
+ if (sizeof(Unit) == 1) {
+ stream->read(_flags, sizeof(_flags));
+ return;
+ }
+
+ for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) {
+ if (sizeof(Unit) == 2)
+ _flags[i] = stream->readUint16BE();
+ else /* if (sizeof(Unit) == 4) */
+ _flags[i] = stream->readUint32BE();
+ }
+ }
+
+ void writeToStream(Common::WriteStream *stream) {
+ // Shortcut
+ if (sizeof(Unit) == 1) {
+ stream->write(_flags, sizeof(_flags));
+ return;
+ }
+
+ for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) {
+ if (sizeof(Unit) == 2)
+ stream->writeUint16BE(_flags[i]);
+ else /* if (sizeof(Unit) == 4) */
+ stream->writeUint32BE(_flags[i]);
+ }
+ }
+
+private:
+ Unit _flags[(kNumFlags - 1) / NUM_FLAGS + 1];
+};
+
+#undef NUM_FLAGS
+#undef BIT_INDEX_SHIFT
+#undef BIT_INDEX_MASK
+
+int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2);
+
+int32 pegasusRound(const int32 a, const int32 b);
+
+uint32 tickCount();
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/plugins_table.h b/engines/plugins_table.h
index fac956755e..010de0d5e2 100644
--- a/engines/plugins_table.h
+++ b/engines/plugins_table.h
@@ -56,6 +56,9 @@ LINK_PLUGIN(MOHAWK)
#if PLUGIN_ENABLED_STATIC(PARALLACTION)
LINK_PLUGIN(PARALLACTION)
#endif
+#if PLUGIN_ENABLED_STATIC(PEGASUS)
+LINK_PLUGIN(PEGASUS)
+#endif
#if PLUGIN_ENABLED_STATIC(QUEEN)
LINK_PLUGIN(QUEEN)
#endif
@@ -89,6 +92,9 @@ LINK_PLUGIN(TINSEL)
#if PLUGIN_ENABLED_STATIC(TOLTECS)
LINK_PLUGIN(TOLTECS)
#endif
+#if PLUGIN_ENABLED_STATIC(TONY)
+LINK_PLUGIN(TONY)
+#endif
#if PLUGIN_ENABLED_STATIC(TOON)
LINK_PLUGIN(TOON)
#endif
@@ -101,3 +107,6 @@ LINK_PLUGIN(TOUCHE)
#if PLUGIN_ENABLED_STATIC(TUCKER)
LINK_PLUGIN(TUCKER)
#endif
+#if PLUGIN_ENABLED_STATIC(WINTERMUTE)
+LINK_PLUGIN(WINTERMUTE)
+#endif
diff --git a/engines/queen/display.cpp b/engines/queen/display.cpp
index 83dc1a9f60..cd9a1075fa 100644
--- a/engines/queen/display.cpp
+++ b/engines/queen/display.cpp
@@ -23,9 +23,13 @@
#include "common/system.h"
#include "common/events.h"
+#include "common/stream.h"
+#include "common/memstream.h"
#include "graphics/cursorman.h"
#include "graphics/palette.h"
+#include "graphics/surface.h"
+#include "graphics/decoders/pcx.h"
#include "queen/display.h"
#include "queen/input.h"
@@ -806,28 +810,22 @@ void Display::fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w,
}
void Display::decodePCX(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd) {
- *w = READ_LE_UINT16(src + 12);
- *h = READ_LE_UINT16(src + 14);
+ Common::MemoryReadStream str(src, srcSize);
+
+ ::Graphics::PCXDecoder pcx;
+ if (!pcx.loadStream(str))
+ error("Error while reading PCX image");
+
+ const ::Graphics::Surface *pcxSurface = pcx.getSurface();
+ if (pcxSurface->format.bytesPerPixel != 1)
+ error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel);
+ *w = pcxSurface->w;
+ *h = pcxSurface->h;
assert(palStart <= palEnd && palEnd <= 256);
- const uint8 *palData = src + srcSize - 768;
- memcpy(pal, palData + palStart * 3, (palEnd - palStart) * 3);
-
- src += 128;
- for (int y = 0; y < *h; ++y) {
- uint8 *p = dst;
- while (p < dst + *w) {
- uint8 col = *src++;
- if ((col & 0xC0) == 0xC0) {
- uint8 len = col & 0x3F;
- memset(p, *src++, len);
- p += len;
- } else {
- *p++ = col;
- }
- }
- dst += dstPitch;
- }
+ memcpy(pal, pcx.getPalette() + palStart * 3, (palEnd - palStart) * 3);
+ for (uint16 y = 0; y < pcxSurface->h; y++)
+ memcpy(dst + y * dstPitch, pcxSurface->getBasePtr(0, y), pcxSurface->w);
}
void Display::decodeLBM(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd, uint8 colorBase) {
diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp
index f3b183c84f..c403536e22 100644
--- a/engines/queen/queen.cpp
+++ b/engines/queen/queen.cpp
@@ -113,12 +113,12 @@ int QueenMetaEngine::getMaximumSaveSlot() const { return 99; }
const ExtraGuiOptions QueenMetaEngine::getExtraGuiOptions(const Common::String &target) const {
Common::String guiOptions;
ExtraGuiOptions options;
-
+
if (target.empty()) {
options.push_back(queenExtraGuiOption);
return options;
}
-
+
if (ConfMan.hasKey("guioptions", target)) {
guiOptions = ConfMan.get("guioptions", target);
guiOptions = parseGameGUIOptions(guiOptions);
diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp
index 96746b538c..3efc554cb3 100644
--- a/engines/saga/script.cpp
+++ b/engines/saga/script.cpp
@@ -948,7 +948,7 @@ void Script::opSpeak(SCRIPTOP_PARAMS) {
// scripts change to scene 5, but do not clear the cutaway that appears
// before Gorrister's speech starts, resulting in a deadlock. We do this
// manually here.
- if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 1 &&
+ if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 1 &&
_vm->_scene->currentSceneNumber() == 5 && _vm->_anim->hasCutaway()) {
_vm->_anim->returnFromCutaway();
}
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 1889d53480..5ae8245e5a 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -2882,17 +2882,17 @@ bool Console::cmdDisassemble(int argc, const char **argv) {
reg_t addr = NULL_REG;
if (!obj) {
- DebugPrintf("Not an object.");
+ DebugPrintf("Not an object.\n");
return true;
}
if (selectorId < 0) {
- DebugPrintf("Not a valid selector name.");
+ DebugPrintf("Not a valid selector name.\n");
return true;
}
if (lookupSelector(_engine->_gamestate->_segMan, objAddr, selectorId, NULL, &addr) != kSelectorMethod) {
- DebugPrintf("Not a method.");
+ DebugPrintf("Not a method.\n");
return true;
}
diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp
index a0f7ebf4a2..3dc042389e 100644
--- a/engines/sci/engine/file.cpp
+++ b/engines/sci/engine/file.cpp
@@ -95,7 +95,7 @@ reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool u
outFile = saveFileMan->openForSaving(wrappedName, isCompressed);
if (!outFile)
debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
-
+
// QfG1 opens the character export file with _K_FILE_MODE_CREATE first,
// closes it immediately and opens it again with this here. Perhaps
// other games use this for read access as well. I guess changing this
@@ -387,7 +387,7 @@ uint32 VirtualIndexFile::read(char *buffer, uint32 size) {
uint32 VirtualIndexFile::write(const char *buffer, uint32 size) {
_changed = true;
uint32 curPos = _ptr - _buffer;
-
+
// Check if the buffer needs to be resized
if (curPos + size >= _bufferSize) {
_bufferSize = curPos + size + 1;
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index f5f46285be..b6b36c47e7 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -524,7 +524,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(PalCycle), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
// SCI2 Empty functions
-
+
// Debug function used to track resources
{ MAP_EMPTY(ResourceTrack), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// Future TODO: This call is used in the floppy version of QFG4 to add
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index f7cc4f44b5..e977f15c0c 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -340,7 +340,7 @@ reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
if (argv[0] == SIGNAL_REG)
return s->r_acc;
-
+
uint16 handle = argv[0].toUint16();
#ifdef ENABLE_SCI32
@@ -624,7 +624,7 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
// Special case for KQ6 Mac: The game checks for two video files to see
// if they exist before it plays them. Since we support multiple naming
// schemes for resource fork files, we also need to support that here in
- // case someone has a "HalfDome.bin" file, etc.
+ // case someone has a "HalfDome.bin" file, etc.
if (!exists && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
(name == "HalfDome" || name == "Kq6Movie"))
exists = Common::MacResManager::exists(name);
@@ -998,7 +998,7 @@ reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv) {
if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
error("kMakeSaveFileName: invalid savegame ID specified");
uint saveSlot = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
-
+
Common::Array<SavegameDesc> saves;
listSavegames(saves);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index 002ef1ff07..4061795f82 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -31,6 +31,9 @@
#include "common/debug-channels.h"
#include "common/list.h"
#include "common/system.h"
+#include "common/math.h"
+
+//#define DEBUG_MERGEPOLY
namespace Sci {
@@ -71,11 +74,25 @@ enum {
struct FloatPoint {
FloatPoint() : x(0), y(0) {}
FloatPoint(float x_, float y_) : x(x_), y(y_) {}
+ FloatPoint(Common::Point p) : x(p.x), y(p.y) {}
Common::Point toPoint() {
return Common::Point((int16)(x + 0.5), (int16)(y + 0.5));
}
+ float operator*(const FloatPoint &p) const {
+ return x*p.x + y*p.y;
+ }
+ FloatPoint operator*(float l) const {
+ return FloatPoint(l*x, l*y);
+ }
+ FloatPoint operator-(const FloatPoint &p) const {
+ return FloatPoint(x-p.x, y-p.y);
+ }
+ float norm() const {
+ return x*x+y*y;
+ }
+
float x, y;
};
@@ -135,15 +152,20 @@ public:
return _head;
}
- void insertHead(Vertex *elm) {
+ void insertAtEnd(Vertex *elm) {
if (_head == NULL) {
elm->_next = elm->_prev = elm;
+ _head = elm;
} else {
elm->_next = _head;
elm->_prev = _head->_prev;
_head->_prev = elm;
elm->_prev->_next = elm;
}
+ }
+
+ void insertHead(Vertex *elm) {
+ insertAtEnd(elm);
_head = elm;
}
@@ -788,10 +810,10 @@ int PathfindingState::findNearPoint(const Common::Point &p, Polygon *polygon, Co
* including the vertices themselves)
* Parameters: (const Common::Point &) a, b: The line segment (a, b)
* (Vertex *) vertex: The first vertex of the edge
- * Returns : (int) FP_OK on success, PF_ERROR otherwise
+ * Returns : (int) PF_OK on success, PF_ERROR otherwise
* (FloatPoint) *ret: The intersection point
*/
-static int intersection(const Common::Point &a, const Common::Point &b, Vertex *vertex, FloatPoint *ret) {
+static int intersection(const Common::Point &a, const Common::Point &b, const Vertex *vertex, FloatPoint *ret) {
// Parameters of parametric equations
float s, t;
// Numerator and denominator of equations
@@ -1783,39 +1805,619 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) {
}
}
+// ==========================================================================
+// kMergePoly utility functions
+
+// Compute square of the distance of p to the segment a-b.
+static float pointSegDistance(const Common::Point &a, const Common::Point &b,
+ const Common::Point &p) {
+ FloatPoint ba(b-a);
+ FloatPoint pa(p-a);
+ FloatPoint bp(b-p);
+
+ // Check if the projection of p on the line a-b lies between a and b
+ if (ba*pa >= 0.0f && ba*bp >= 0.0f) {
+ // If yes, return the (squared) distance of p to the line a-b:
+ // translate a to origin, project p and subtract
+ float linedist = (ba*((ba*pa)/(ba*ba)) - pa).norm();
+
+ return linedist;
+ } else {
+ // If no, return the (squared) distance to either a or b, whichever
+ // is closest.
+
+ // distance to a:
+ float adist = pa.norm();
+ // distance to b:
+ float bdist = FloatPoint(p-b).norm();
+
+ return MIN(adist, bdist);
+ }
+}
+
+// find intersection between edges of two polygons.
+// endpoints count, except v2->_next
+static bool segSegIntersect(const Vertex *v1, const Vertex *v2, Common::Point &intp) {
+ const Common::Point &a = v1->v;
+ const Common::Point &b = v1->_next->v;
+ const Common::Point &c = v2->v;
+ const Common::Point &d = v2->_next->v;
+
+ // First handle the endpoint cases manually
+
+ if (collinear(a, b, c) && collinear(a, b, d))
+ return false;
+
+ if (collinear(a, b, c)) {
+ // a, b, c collinear
+ // return true/c if c is between a and b
+ intp = c;
+ if (a.x != b.x) {
+ if ((a.x <= c.x && c.x <= b.x) || (b.x <= c.x && c.x <= a.x))
+ return true;
+ } else {
+ if ((a.y <= c.y && c.y <= b.y) || (b.y <= c.y && c.y <= a.y))
+ return true;
+ }
+ }
+
+ if (collinear(a, b, d)) {
+ intp = d;
+ // a, b, d collinear
+ // return false/d if d is between a and b
+ if (a.x != b.x) {
+ if ((a.x <= d.x && d.x <= b.x) || (b.x <= d.x && d.x <= a.x))
+ return false;
+ } else {
+ if ((a.y <= d.y && d.y <= b.y) || (b.y <= d.y && d.y <= a.y))
+ return false;
+ }
+ }
+
+ int len_dc = c.sqrDist(d);
+
+ if (!len_dc) error("zero length edge in polygon");
+
+ if (pointSegDistance(c, d, a) <= 2.0f) {
+ intp = a;
+ return true;
+ }
+
+ if (pointSegDistance(c, d, b) <= 2.0f) {
+ intp = b;
+ return true;
+ }
+
+ // If not an endpoint, call the generic intersection function
+
+ FloatPoint p;
+ if (intersection(a, b, v2, &p) == PF_OK) {
+ intp = p.toPoint();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// For intersecting polygon segments, determine if
+// * the v2 edge enters polygon 1 at this intersection: positive return value
+// * the v2 edge and the v1 edges are parallel: zero return value
+// * the v2 edge exits polygon 1 at this intersection: negative return value
+static int intersectDir(const Vertex *v1, const Vertex *v2) {
+ Common::Point p1 = v1->_next->v - v1->v;
+ Common::Point p2 = v2->_next->v - v2->v;
+ return (p1.x*p2.y - p2.x*p1.y);
+}
+
+// Direction of edge in degrees from pos. x-axis, between -180 and 180
+static int edgeDir(const Vertex *v) {
+ Common::Point p = v->_next->v - v->v;
+ int deg = (int)Common::rad2deg(atan2((double)p.y, (double)p.x));
+ if (deg < -180) deg += 360;
+ if (deg > 180) deg -= 360;
+ return deg;
+}
+
+// For points p1, p2 on the polygon segment v, determine if
+// * p1 lies before p2: negative return value
+// * p1 and p2 are the same: zero return value
+// * p1 lies after p2: positive return value
+static int liesBefore(const Vertex *v, const Common::Point &p1, const Common::Point &p2) {
+ return v->v.sqrDist(p1) - v->v.sqrDist(p2);
+}
+
+// Structure describing an "extension" to the work polygon following edges
+// of the polygon being merged.
+
+// The patch begins on the point intersection1, being the intersection
+// of the edges starting at indexw1/vertexw1 on the work polygon, and at
+// indexp1/vertexp1 on the polygon being merged.
+// It ends with the point intersection2, being the analogous intersection.
+struct Patch {
+ unsigned int indexw1;
+ unsigned int indexp1;
+ const Vertex *vertexw1;
+ const Vertex *vertexp1;
+ Common::Point intersection1;
+
+ unsigned int indexw2;
+ unsigned int indexp2;
+ const Vertex *vertexw2;
+ const Vertex *vertexp2;
+ Common::Point intersection2;
+
+ bool disabled; // If true, this Patch was made superfluous by another Patch
+};
+
+
+// Check if the given vertex on the work polygon is bypassed by this patch.
+static bool isVertexCovered(const Patch &p, unsigned int wi) {
+
+ // / v (outside)
+ // ---w1--1----p----w2--2----
+ // ^ \ (inside)
+ if (wi > p.indexw1 && wi <= p.indexw2)
+ return true;
+
+ // v / (outside)
+ // ---w2--2----p----w1--1----
+ // \ ^ (inside)
+ if (p.indexw1 > p.indexw2 && (wi <= p.indexw2 || wi > p.indexw1))
+ return true;
+
+ // v / (outside)
+ // ---w1--2--1-------p-----
+ // w2 \ ^ (inside)
+ if (p.indexw1 == p.indexw2 && liesBefore(p.vertexw1, p.intersection1, p.intersection2) > 0)
+ return true; // This patch actually covers _all_ vertices on work
+
+ return false;
+}
+
+// Check if patch p1 makes patch p2 superfluous.
+static bool isPatchCovered(const Patch &p1, const Patch &p2) {
+
+ // Same exit and entry points
+ if (p1.intersection1 == p2.intersection1 && p1.intersection2 == p2.intersection2)
+ return true;
+
+ // / * v (outside)
+ // ---p1w1--1----p2w1-1---p1w2--2----
+ // ^ * \ (inside)
+ if (p1.indexw1 < p2.indexw1 && p2.indexw1 < p1.indexw2)
+ return true;
+ if (p1.indexw1 > p1.indexw2 && (p2.indexw1 > p1.indexw1 || p2.indexw1 < p1.indexw2))
+ return true;
+
+
+ // / * v (outside)
+ // ---p1w1--11----p2w2-2---p1w2--12----
+ // ^ * \ (inside)
+ if (p1.indexw1 < p2.indexw2 && p2.indexw2 < p1.indexw2)
+ return true;
+ if (p1.indexw1 > p1.indexw2 && (p2.indexw2 > p1.indexw1 || p2.indexw2 < p1.indexw2))
+ return true;
+
+ // Opposite of two above situations
+ if (p2.indexw1 < p1.indexw1 && p1.indexw1 < p2.indexw2)
+ return false;
+ if (p2.indexw1 > p2.indexw2 && (p1.indexw1 > p2.indexw1 || p1.indexw1 < p2.indexw2))
+ return false;
+
+ if (p2.indexw1 < p1.indexw2 && p1.indexw2 < p2.indexw2)
+ return false;
+ if (p2.indexw1 > p2.indexw2 && (p1.indexw2 > p2.indexw1 || p1.indexw2 < p2.indexw2))
+ return false;
+
+
+ // The above checks covered the cases where one patch covers the other and
+ // the intersections of the patches are on different edges.
+
+ // So, if we passed the above checks, we have to check the order of
+ // intersections on edges.
+
+
+ if (p1.indexw1 != p1.indexw2) {
+
+ // / * v (outside)
+ // ---p1w1--11---21--------p1w2--2----
+ // p2w1 ^ * \ (inside)
+ if (p1.indexw1 == p2.indexw1)
+ return (liesBefore(p1.vertexw1, p1.intersection1, p2.intersection1) < 0);
+
+ // / * v (outside)
+ // ---p1w1--11---------p1w2--21---12----
+ // ^ p2w1 * \ (inside)
+ if (p1.indexw2 == p2.indexw1)
+ return (liesBefore(p1.vertexw2, p1.intersection2, p2.intersection1) > 0);
+
+ // If neither of the above, then the intervals of the polygon
+ // covered by patch1 and patch2 are disjoint
+ return false;
+ }
+
+ // p1w1 == p1w2
+ // Also, p1w1/p1w2 isn't strictly between p2
+
+
+ // v / * (outside)
+ // ---p1w1--12--11-------p2w1-21----
+ // p1w2 \ ^ * (inside)
+
+ // v / / (outside)
+ // ---p1w1--12--21--11---------
+ // p1w2 \ ^ ^ (inside)
+ // p2w1
+ if (liesBefore(p1.vertexw1, p1.intersection1, p1.intersection2) > 0)
+ return (p1.indexw1 != p2.indexw1);
+
+ // CHECKME: This is meaningless if p2w1 != p2w2 ??
+ if (liesBefore(p2.vertexw1, p2.intersection1, p2.intersection2) > 0)
+ return false;
+
+ // CHECKME: This is meaningless if p1w1 != p2w1 ??
+ if (liesBefore(p2.vertexw1, p2.intersection1, p1.intersection1) <= 0)
+ return false;
+
+ // CHECKME: This is meaningless if p1w2 != p2w1 ??
+ if (liesBefore(p2.vertexw1, p2.intersection1, p1.intersection2) >= 0)
+ return false;
+
+ return true;
+}
+
+// Merge a single polygon into the work polygon.
+// If there is an intersection between work and polygon, this function
+// returns true, and replaces the vertex list of work by an extended version,
+// that covers polygon.
+//
+// NOTE: The strategy used matches qfg1new closely, and is a bit error-prone.
+// A more robust strategy would be inserting all intersection points directly
+// into both vertex lists as a first pass. This would make finding the merged
+// polygon a much more straightforward edge-walk, and avoid cases where SSCI's
+// algorithm mixes up the order of multiple intersections on a single edge.
+bool mergeSinglePolygon(Polygon &work, const Polygon &polygon) {
+#ifdef DEBUG_MERGEPOLY
+ const Vertex *vertex;
+ debugN("work:");
+ CLIST_FOREACH(vertex, &(work.vertices)) {
+ debugN(" (%d,%d) ", vertex->v.x, vertex->v.y);
+ }
+ debugN("\n");
+ debugN("poly:");
+ CLIST_FOREACH(vertex, &(polygon.vertices)) {
+ debugN(" (%d,%d) ", vertex->v.x, vertex->v.y);
+ }
+ debugN("\n");
+#endif
+ uint workSize = work.vertices.size();
+ uint polygonSize = polygon.vertices.size();
+
+ int patchCount = 0;
+ Patch patchList[8];
+
+ const Vertex *workv = work.vertices._head;
+ const Vertex *polyv = polygon.vertices._head;
+ for (uint wi = 0; wi < workSize; ++wi, workv = workv->_next) {
+ for (uint pi = 0; pi < polygonSize; ++pi, polyv = polyv->_next) {
+ Common::Point intersection1;
+ Common::Point intersection2;
+
+ bool intersects = segSegIntersect(workv, polyv, intersection1);
+ if (!intersects)
+ continue;
+
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: intersection at work %d, poly %d", wi, pi);
+#endif
+
+ if (intersectDir(workv, polyv) >= 0)
+ continue;
+
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: intersection in right direction");
+#endif
+
+ int angle = 0;
+ int baseAngle = edgeDir(workv);
+
+ // We now found the point where an edge of 'polygon' left 'work'.
+ // Now find the re-entry point.
+
+ // NOTE: The order in which this searches does not always work
+ // properly if the correct patch would only use a single partial
+ // edge of poly. Because it starts at polyv->_next, it will skip
+ // the correct re-entry and proceed to the next.
+
+ const Vertex *workv2;
+ const Vertex *polyv2 = polyv->_next;
+
+ intersects = false;
+
+ uint pi2, wi2;
+ for (pi2 = 0; pi2 < polygonSize; ++pi2, polyv2 = polyv2->_next) {
+
+ int newAngle = edgeDir(polyv2);
+
+ int relAngle = newAngle - baseAngle;
+ if (relAngle > 180) relAngle -= 360;
+ if (relAngle < -180) relAngle += 360;
+
+ angle += relAngle;
+ baseAngle = newAngle;
+
+ workv2 = workv;
+ for (wi2 = 0; wi2 < workSize; ++wi2, workv2 = workv2->_next) {
+ intersects = segSegIntersect(workv2, polyv2, intersection2);
+ if (!intersects)
+ continue;
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: re-entry intersection at work %d, poly %d", (wi + wi2) % workSize, (pi + 1 + pi2) % polygonSize);
+#endif
+
+ if (intersectDir(workv2, polyv2) > 0) {
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: re-entry intersection in right direction, angle = %d", angle);
+#endif
+ break; // found re-entry point
+ }
+
+ }
+
+ if (intersects)
+ break;
+
+ }
+
+ if (!intersects || angle < 0)
+ continue;
+
+
+ if (patchCount >= 8)
+ error("kMergePoly: Too many patches");
+
+ // convert relative to absolute vertex indices
+ pi2 = (pi + 1 + pi2) % polygonSize;
+ wi2 = (wi + wi2) % workSize;
+
+ Patch &newPatch = patchList[patchCount];
+ newPatch.indexw1 = wi;
+ newPatch.vertexw1 = workv;
+ newPatch.indexp1 = pi;
+ newPatch.vertexp1 = polyv;
+ newPatch.intersection1 = intersection1;
+
+ newPatch.indexw2 = wi2;
+ newPatch.vertexw2 = workv2;
+ newPatch.indexp2 = pi2;
+ newPatch.vertexp2 = polyv2;
+ newPatch.intersection2 = intersection2;
+ newPatch.disabled = false;
+
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: adding patch at work %d, poly %d", wi, pi);
+#endif
+
+ if (patchCount == 0) {
+ patchCount++;
+ continue;
+ }
+
+ bool necessary = true;
+ for (int i = 0; i < patchCount; ++i) {
+ if (isPatchCovered(patchList[i], newPatch)) {
+ necessary = false;
+ break;
+ }
+ }
+
+ if (!necessary)
+ continue;
+
+ patchCount++;
+
+ if (patchCount > 1) {
+ // check if this patch makes other patches superfluous
+ for (int i = 0; i < patchCount-1; ++i)
+ if (isPatchCovered(newPatch, patchList[i]))
+ patchList[i].disabled = true;
+ }
+ }
+ }
+
+
+ if (patchCount == 0)
+ return false; // nothing changed
+
+
+ // Determine merged work by doing a walk over the edges
+ // of work, crossing over to polygon when encountering a patch.
+
+ Polygon output(0);
+
+ workv = work.vertices._head;
+ for (uint wi = 0; wi < workSize; ++wi, workv = workv->_next) {
+
+ bool covered = false;
+ for (int p = 0; p < patchCount; ++p) {
+ if (patchList[p].disabled) continue;
+ if (isVertexCovered(patchList[p], wi)) {
+ covered = true;
+ break;
+ }
+ }
+
+ if (!covered) {
+ // Add vertex to output
+ output.vertices.insertAtEnd(new Vertex(workv->v));
+ }
+
+
+ // CHECKME: Why is this the correct order in which to process
+ // the patches? (What if two of them start on this line segment
+ // in the opposite order?)
+
+ for (int p = 0; p < patchCount; ++p) {
+
+ const Patch &patch = patchList[p];
+ if (patch.disabled) continue;
+ if (patch.indexw1 != wi) continue;
+ if (patch.intersection1 != workv->v) {
+ // Add intersection point to output
+ output.vertices.insertAtEnd(new Vertex(patch.intersection1));
+ }
+
+ // Add vertices from polygon between vertexp1 (excl) and vertexp2 (incl)
+ for (polyv = patch.vertexp1->_next; polyv != patch.vertexp2; polyv = polyv->_next)
+ output.vertices.insertAtEnd(new Vertex(polyv->v));
+
+ output.vertices.insertAtEnd(new Vertex(patch.vertexp2->v));
+
+ if (patch.intersection2 != patch.vertexp2->v) {
+ // Add intersection point to output
+ output.vertices.insertAtEnd(new Vertex(patch.intersection2));
+ }
+
+ // TODO: We could continue after the re-entry point here?
+ }
+ }
+ // Remove last vertex if it's the same as the first vertex
+ if (output.vertices._head->v == output.vertices._head->_prev->v)
+ output.vertices.remove(output.vertices._head->_prev);
+
+
+ // Slight hack: swap vertex lists of output and work polygons.
+ SWAP(output.vertices._head, work.vertices._head);
+
+ return true;
+}
+
+
/**
* This is a quite rare kernel function. An example of when it's called
* is in QFG1VGA, after killing any monster.
+ *
+ * It takes a polygon, and extends it to also cover any polygons from the
+ * input list with which it intersects. Any of those polygons so covered
+ * from the input list are marked by adding 0x10 to their type field.
*/
reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) {
-#if 0
// 3 parameters: raw polygon data, polygon list, list size
reg_t polygonData = argv[0];
List *list = s->_segMan->lookupList(argv[1]);
- Node *node = s->_segMan->lookupNode(list->first);
- // List size is not needed
- Polygon *polygon;
- int count = 0;
+ // The size of the "work" point list SSCI uses. We use a dynamic one instead
+ //reg_t listSize = argv[2];
+
+ SegmentRef pointList = s->_segMan->dereference(polygonData);
+ if (!pointList.isValid() || pointList.skipByte) {
+ warning("kMergePoly: Polygon data pointer is invalid");
+ return make_reg(0, 0);
+ }
+
+ Node *node;
+
+#ifdef DEBUG_MERGEPOLY
+ node = s->_segMan->lookupNode(list->first);
+ while (node) {
+ draw_polygon(s, node->value, 320, 190);
+ node = s->_segMan->lookupNode(node->succ);
+ }
+ Common::Point prev, first;
+ prev = first = readPoint(pointList, 0);
+ for (int i = 1; readPoint(pointList, i).x != 0x7777; i++) {
+ Common::Point point = readPoint(pointList, i);
+ draw_line(s, prev, point, 1, 320, 190);
+ prev = point;
+ }
+ draw_line(s, prev, first, 1, 320, 190);
+ // Update the whole screen
+ g_sci->_gfxScreen->copyToScreen();
+ g_system->updateScreen();
+ g_system->delayMillis(1000);
+#endif
+
+ // The work polygon which we're going to merge with the polygons in list
+ Polygon work(0);
+
+ for (int i = 0; true; ++i) {
+ Common::Point p = readPoint(pointList, i);
+ if (p.x == POLY_LAST_POINT)
+ break;
+ Vertex *vertex = new Vertex(p);
+ work.vertices.insertAtEnd(vertex);
+ }
+
+ // TODO: Check behaviour for single-vertex polygons
+ node = s->_segMan->lookupNode(list->first);
while (node) {
- polygon = convert_polygon(s, node->value);
+ Polygon *polygon = convert_polygon(s, node->value);
if (polygon) {
- count += readSelectorValue(s->_segMan, node->value, SELECTOR(size));
+ // CHECKME: Confirm vertex order that convert_polygon and
+ // fix_vertex_order output. For now, we re-reverse the order since
+ // convert_polygon reads the vertices reversed, and fix up head.
+ polygon->vertices.reverse();
+ polygon->vertices._head = polygon->vertices._head->_next;
+
+ // Merge this polygon into the work polygon if there is an
+ // intersection.
+ bool intersected = mergeSinglePolygon(work, *polygon);
+
+ // If so, flag it
+ if (intersected) {
+ writeSelectorValue(s->_segMan, node->value,
+ SELECTOR(type), polygon->type + 0x10);
+#ifdef DEBUG_MERGEPOLY
+ debugN("Merged polygon: ");
+ // Iterate over edges
+ Vertex *vertex;
+ CLIST_FOREACH(vertex, &(work.vertices)) {
+ debugN(" (%d,%d) ", vertex->v.x, vertex->v.y);
+ }
+ debugN("\n");
+#endif
+ }
}
node = s->_segMan->lookupNode(node->succ);
}
-#endif
- // TODO: actually merge the polygon. We return an empty polygon for now.
- // In QFG1VGA, you can walk over enemy bodies after killing them, since
- // this is a stub.
- reg_t output = allocateOutputArray(s->_segMan, 1);
+
+ // Allocate output array
+ reg_t output = allocateOutputArray(s->_segMan, work.vertices.size()+1);
SegmentRef arrayRef = s->_segMan->dereference(output);
- writePoint(arrayRef, 0, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT));
- warning("Stub: kMergePoly");
+
+ // Copy work.vertices into arrayRef
+ Vertex *vertex;
+ unsigned int n = 0;
+ CLIST_FOREACH(vertex, &work.vertices) {
+ if (vertex == work.vertices._head || vertex->v != vertex->_prev->v)
+ writePoint(arrayRef, n++, vertex->v);
+ }
+
+ writePoint(arrayRef, n, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT));
+
+#ifdef DEBUG_MERGEPOLY
+ prev = first = readPoint(arrayRef, 0);
+ for (int i = 1; readPoint(arrayRef, i).x != 0x7777; i++) {
+ Common::Point point = readPoint(arrayRef, i);
+ draw_line(s, prev, point, 3, 320, 190);
+ prev = point;
+ }
+
+ draw_line(s, prev, first, 3, 320, 190);
+
+ // Update the whole screen
+ g_sci->_gfxScreen->copyToScreen();
+ g_system->updateScreen();
+ if (!g_sci->_gfxPaint16)
+ g_system->delayMillis(1000);
+
+ debug("kMergePoly done");
+#endif
+
return output;
}
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index c22d7c7b1e..c4db0b891c 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -165,6 +165,7 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) {
// do clipping. In SQ4 we get the door code in here and that's even
// larger than uint32!
if (*source == '-') {
+ // FIXME: Setting result to -1 does _not_ negate the output.
result = -1;
source++;
}
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 6bf9aff2fe..9b0cb38f51 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -275,7 +275,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
// Signal the engine scripts that the video is done
writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG);
} else {
- writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG);
+ writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG);
}
break;
default:
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 037f4ab700..36d2841b07 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -144,7 +144,7 @@ void Script::load(int script_nr, ResourceManager *resMan) {
_heapStart = _buf + _scriptSize;
- assert(_bufSize - _scriptSize <= heap->size);
+ assert(_bufSize - _scriptSize >= heap->size);
memcpy(_heapStart, heap->data, heap->size);
}
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 659c13b13e..8454be514a 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -915,9 +915,53 @@ const uint16 qfg3PatchImportDialog[] = {
PATCH_END
};
+
+
+// ===========================================================================
+// Patch for the Woo dialog option in Uhura's conversation. Bug #3040722
+// Problem: The Woo dialog option (0xffb5) is negative, and therefore
+// treated as an option opening a submenu. This leads to uhuraTell::doChild
+// being called, which calls hero::solvePuzzle and then proceeds with
+// Teller::doChild to open the submenu. However, there is no actual submenu
+// defined for option -75 since -75 does not show up in uhuraTell::keys.
+// This will cause Teller::doChild to run out of bounds while scanning through
+// uhuraTell::keys.
+// Strategy: there is another conversation option in uhuraTell::doChild calling
+// hero::solvePuzzle (0xfffc) which does a ret afterwards without going to
+// Teller::doChild. We jump to this call of hero::solvePuzzle to get that same
+// behaviour.
+
+const byte qfg3SignatureWooDialog[] = {
+ 30,
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0xb6, // ldi b6
+ 0x1a, // eq?
+ 0x2f, 0x05, // bt 05
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0x9b, // ldi 9b
+ 0x1a, // eq?
+ 0x31, 0x0c, // bnt 0c
+ 0x38, 0x97, 0x02, // pushi 0297
+ 0x7a, // push2
+ 0x38, 0x0c, 0x01, // pushi 010c
+ 0x7a, // push2
+ 0x81, 0x00, // lag 00
+ 0x4a, 0x08, // send 08
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0xb5, // ldi b5
+ 0
+};
+
+const uint16 qfg3PatchWooDialog[] = {
+ PATCH_ADDTOOFFSET | +0x29,
+ 0x33, 0x11, // jmp to 0x6a2, the call to hero::solvePuzzle for 0xFFFC
+ PATCH_END
+};
+
// script, description, magic DWORD, adjust
const SciScriptSignature qfg3Signatures[] = {
{ 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
+ { 440, "dialog crash when asking about Woo", 1, PATCH_MAGICDWORD(0x67, 0x12, 0x35, 0xb5), -26, qfg3SignatureWooDialog, qfg3PatchWooDialog },
SCI_SIGNATUREENTRY_TERMINATOR
};
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index d3abb9ff41..b2f22aa985 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -514,7 +514,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
if (!objType) {
debugN("End of script object (#0) encountered.\n");
- debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)",
+ debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)\n",
objectctr[6], objectctr[1], objectctr[7], objectctr[10]);
return;
}
@@ -527,7 +527,8 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
_seeker += objsize;
- objectctr[objType]++;
+ if (objType >= 0 && objType < ARRAYSIZE(objectctr))
+ objectctr[objType]++;
switch (objType) {
case SCI_OBJ_OBJECT:
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 951fc7c363..04c1dab158 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -825,7 +825,7 @@ byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
}
bool SegManager::freeDynmem(reg_t addr) {
- if (addr.getSegment() < 1 || addr.getSegment() >= _heap.size() ||
+ if (addr.getSegment() < 1 || addr.getSegment() >= _heap.size() ||
!_heap[addr.getSegment()] || _heap[addr.getSegment()]->getType() != SEG_TYPE_DYNMEM)
return false; // error
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 3f43966976..ef8f165084 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -228,7 +228,7 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP
uint32 exportAddr = scr->validateExportFunc(pubfunct, false);
if (!exportAddr)
return NULL;
-
+
// Check if a breakpoint is set on this method
g_sci->checkExportBreakpoint(script, pubfunct);
diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp
index ad1d9e8623..5535a7408a 100644
--- a/engines/sci/graphics/controls32.cpp
+++ b/engines/sci/graphics/controls32.cpp
@@ -68,7 +68,7 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) {
while (captureEvents) {
curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
-
+
if (curEvent.type == SCI_EVENT_NONE) {
eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
} else {
@@ -170,11 +170,11 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) {
// Note: the following checkAltInput call might make the text
// too wide to fit, but SSCI fails to check that too.
}
-
+
reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap));
Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject);
//texteditCursorErase(); // TODO: Cursor
-
+
// Write back string
_segMan->strcpy(textReference, text.c_str());
// Modify the buffer and show it
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 968014c032..8b7fa2c384 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -197,7 +197,7 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
} else {
it->planeOffsetX = 0;
}
-
+
if (it->planeRect.top < 0) {
it->planeOffsetY = -it->planeRect.top;
it->planeRect.top = 0;
@@ -420,7 +420,7 @@ void GfxFrameout::deletePlaneItems(reg_t planeObject) {
} else {
objectMatches = true;
}
-
+
if (objectMatches) {
FrameoutEntry *itemEntry = *listIterator;
listIterator = _screenItems.erase(listIterator);
@@ -661,7 +661,7 @@ void GfxFrameout::kernelFrameout() {
if (!itemEntry->visible)
continue;
-
+
if (itemEntry->object.isNull()) {
// Picture cel data
_coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x);
@@ -703,7 +703,7 @@ void GfxFrameout::kernelFrameout() {
view->getCelRect(itemEntry->loopNo, itemEntry->celNo,
itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect);
else
- view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo,
+ view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo,
itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX,
itemEntry->scaleY, itemEntry->celRect);
@@ -755,10 +755,10 @@ void GfxFrameout::kernelFrameout() {
if (view) {
if (!clipRect.isEmpty()) {
if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->draw(itemEntry->celRect, clipRect, translatedClipRect,
+ view->draw(itemEntry->celRect, clipRect, translatedClipRect,
itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires());
else
- view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect,
+ view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect,
itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY);
}
}
@@ -817,7 +817,7 @@ void GfxFrameout::printPlaneItemList(Console *con, reg_t planeObject) {
for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
FrameoutEntry *e = *listIterator;
reg_t itemPlane = readSelector(_segMan, e->object, SELECTOR(plane));
-
+
if (planeObject == itemPlane) {
Common::String curItemName = _segMan->getObjectName(e->object);
Common::Rect icr = e->celRect;
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index 89f3625e2c..d20aa80c77 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -559,8 +559,8 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
SciTrackOriginReply originReply;
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply);
if (solution.type == WORKAROUND_NONE)
- error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)",
- PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(),
+ error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)",
+ PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(),
originReply.scriptNr, originReply.localCallOffset);
assert(solution.type == WORKAROUND_IGNORE);
break;
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index 6b4c8180bf..8acdeed763 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -380,17 +380,50 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
int16 oldtop = pwnd->dims.top;
int16 oldleft = pwnd->dims.left;
- if (wmprect.top > pwnd->dims.top)
+ // WORKAROUND: We also adjust the restore rect when adjusting the window
+ // rect.
+ // SSCI does not do this. It wasn't necessary in the original interpreter,
+ // but it is needed for Freddy Pharkas CD. This version does not normally
+ // have text, but we allow this by modifying the text/speech setting
+ // according to what is set in the ScummVM GUI (refer to syncIngameAudioOptions()
+ // in sci.cpp). Since the text used in Freddy Pharkas CD is quite large in
+ // some cases, it ends up being offset in order to fit inside the screen,
+ // but the associated restore rect isn't adjusted accordingly, leading to
+ // artifacts being left on screen when some text boxes are removed. The
+ // fact that the restore rect wasn't ever adjusted doesn't make sense, and
+ // adjusting it shouldn't have any negative side-effects (it *should* be
+ // adjusted, normally, but SCI doesn't do it). The big text boxes are still
+ // odd-looking, because the text rect is drawn outside the text window rect,
+ // but at least there aren't any leftover textbox artifacts left when the
+ // boxes are removed. Adjusting the text window rect would require more
+ // invasive changes than this one, thus it's not really worth the effort
+ // for a feature that was not present in the original game, and its
+ // implementation is buggy in the first place.
+ // Adjusting the restore rect properly fixes bug #3575276.
+
+ if (wmprect.top > pwnd->dims.top) {
pwnd->dims.moveTo(pwnd->dims.left, wmprect.top);
+ if (restoreRect)
+ pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.top);
+ }
- if (wmprect.bottom < pwnd->dims.bottom)
+ if (wmprect.bottom < pwnd->dims.bottom) {
pwnd->dims.moveTo(pwnd->dims.left, wmprect.bottom - pwnd->dims.bottom + pwnd->dims.top);
+ if (restoreRect)
+ pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.bottom - pwnd->restoreRect.bottom + pwnd->restoreRect.top);
+ }
- if (wmprect.right < pwnd->dims.right)
+ if (wmprect.right < pwnd->dims.right) {
pwnd->dims.moveTo(wmprect.right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top);
+ if (restoreRect)
+ pwnd->restoreRect.moveTo(wmprect.right + pwnd->restoreRect.left - pwnd->restoreRect.right, pwnd->restoreRect.top);
+ }
- if (wmprect.left > pwnd->dims.left)
+ if (wmprect.left > pwnd->dims.left) {
pwnd->dims.moveTo(wmprect.left, pwnd->dims.top);
+ if (restoreRect)
+ pwnd->restoreRect.moveTo(wmprect.left, pwnd->restoreRect.top);
+ }
pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop);
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 42ae00b525..15b18ce8e6 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -876,6 +876,7 @@ void SciEngine::syncIngameAudioOptions() {
if (getGameId() == GID_SQ4
|| getGameId() == GID_FREDDYPHARKAS
|| getGameId() == GID_ECOQUEST
+ || getGameId() == GID_LSL6
// TODO: The following need script patches for simultaneous speech and subtitles
//|| getGameId() == GID_KQ6
//|| getGameId() == GID_LAURABOW2
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 9f18219cb7..3441e26c01 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -228,6 +228,26 @@ public:
bool canLoadGameStateCurrently();
bool canSaveGameStateCurrently();
void syncSoundSettings();
+
+ /**
+ * Syncs the audio options of the ScummVM launcher (speech, subtitles or
+ * both) with the in-game audio options of certain CD game versions. For
+ * some games, this allows simultaneous playing of speech and subtitles,
+ * even if the original games didn't support this feature.
+ *
+ * SCI1.1 games which support simultaneous speech and subtitles:
+ * - EcoQuest 1 CD
+ * - Leisure Suit Larry 6 CD
+ * SCI1.1 games which don't support simultaneous speech and subtitles,
+ * and we add this functionality in ScummVM:
+ * - Space Quest 4 CD
+ * - Freddy Pharkas CD
+ * SCI1.1 games which don't support simultaneous speech and subtitles,
+ * and we haven't added any extra functionality in ScummVM because extra
+ * script patches are needed:
+ * - Laura Bow 2 CD
+ * - King's Quest 6 CD
+ */
void syncIngameAudioOptions();
const SciGameId &getGameId() const { return _gameId; }
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index db9317e071..191e13db0a 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -807,6 +807,9 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) {
int size = f.size();
const uint patchSize = 1344;
+ // Note: Funseeker's Guide also has another version of adl.drv, 8803 bytes.
+ // This isn't supported, but it's not really used anywhere, as that demo
+ // doesn't have sound anyway.
if ((size == 5684) || (size == 5720) || (size == 5727)) {
byte *buf = new byte[patchSize];
diff --git a/engines/sci/sound/drivers/fmtowns.cpp b/engines/sci/sound/drivers/fmtowns.cpp
index 6d8bb2e525..21cb2f1e43 100644
--- a/engines/sci/sound/drivers/fmtowns.cpp
+++ b/engines/sci/sound/drivers/fmtowns.cpp
@@ -52,8 +52,8 @@ public:
uint16 _duration;
private:
- uint8 _id;
- uint8 _velo;
+ uint8 _id;
+ uint8 _velo;
uint8 _program;
MidiDriver_FMTowns *_drv;
@@ -76,7 +76,7 @@ public:
void addChannels(int num);
void dropChannels(int num);
-
+
uint8 currentProgram() const;
private:
@@ -132,7 +132,7 @@ private:
TownsMidiPart **_parts;
TownsChannel **_out;
-
+
uint8 _masterVolume;
bool _soundOn;
@@ -590,7 +590,7 @@ void MidiDriver_FMTowns::addMissingChannels() {
avlChan -= _parts[i]->_chanMissing;
uint8 m = _parts[i]->_chanMissing;
_parts[i]->_chanMissing = 0;
- _parts[i]->addChannels(m);
+ _parts[i]->addChannels(m);
} else {
_parts[i]->_chanMissing -= avlChan;
_parts[i]->addChannels(avlChan);
@@ -601,7 +601,7 @@ void MidiDriver_FMTowns::addMissingChannels() {
void MidiDriver_FMTowns::updateParser() {
if (_timerProc)
- _timerProc(_timerProcPara);
+ _timerProc(_timerProcPara);
}
void MidiDriver_FMTowns::updateChannels() {
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index 422948f975..4e54797960 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -98,7 +98,7 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in
midiMixChannels();
}
- _num_tracks = 1;
+ _numTracks = 1;
_tracks[0] = _mixedData;
if (_pSnd)
setTrack(0);
@@ -144,7 +144,7 @@ byte *MidiParser_SCI::midiMixChannels() {
_mixedData = outData;
long ticker = 0;
byte channelNr, curDelta;
- byte midiCommand = 0, midiParam, global_prev = 0;
+ byte midiCommand = 0, midiParam, globalPrev = 0;
long newDelta;
SoundResource::Channel *channel;
@@ -190,13 +190,13 @@ byte *MidiParser_SCI::midiMixChannels() {
byte midiChannel = midiCommand & 0xF;
_channelUsed[midiChannel] = true;
- if (midiCommand != global_prev)
+ if (midiCommand != globalPrev)
*outData++ = midiCommand;
*outData++ = midiParam;
if (nMidiParams[(midiCommand >> 4) - 8] == 2)
*outData++ = channel->data[channel->curPos++];
channel->prev = midiCommand;
- global_prev = midiCommand;
+ globalPrev = midiCommand;
}
}
@@ -372,8 +372,8 @@ void MidiParser_SCI::unloadMusic() {
resetTracking();
allNotesOff();
}
- _num_tracks = 0;
- _active_track = 255;
+ _numTracks = 0;
+ _activeTrack = 255;
_resetOnPause = false;
if (_mixedData) {
@@ -454,26 +454,26 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
debugC(4, kDebugLevelSound, "signal %04x", _signalToSet);
}
- info.start = _position._play_pos;
+ info.start = _position._playPos;
info.delta = 0;
- while (*_position._play_pos == 0xF8) {
+ while (*_position._playPos == 0xF8) {
info.delta += 240;
- _position._play_pos++;
+ _position._playPos++;
}
- info.delta += *(_position._play_pos++);
+ info.delta += *(_position._playPos++);
// Process the next info.
- if ((_position._play_pos[0] & 0xF0) >= 0x80)
- info.event = *(_position._play_pos++);
+ if ((_position._playPos[0] & 0xF0) >= 0x80)
+ info.event = *(_position._playPos++);
else
- info.event = _position._running_status;
+ info.event = _position._runningStatus;
if (info.event < 0x80)
return;
- _position._running_status = info.event;
+ _position._runningStatus = info.event;
switch (info.command()) {
case 0xC:
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
if (info.channel() == 0xF) {// SCI special case
if (info.basic.param1 != kSetSignalLoop) {
@@ -488,23 +488,23 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
// in glitches (e.g. the intro of LB1 Amiga gets stuck - bug
// #3297883). Refer to MusicEntry::setSignal() in sound/music.cpp.
if (_soundVersion <= SCI_VERSION_0_LATE ||
- _position._play_tick || info.delta) {
+ _position._playTick || info.delta) {
_signalSet = true;
_signalToSet = info.basic.param1;
}
} else {
- _loopTick = _position._play_tick + info.delta;
+ _loopTick = _position._playTick + info.delta;
}
}
break;
case 0xD:
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
case 0xB:
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
// Reference for some events:
// http://wiki.scummvm.org/index.php/SCI/Specifications/Sound/SCI0_Resource_Format#Status_Reference
@@ -588,8 +588,8 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
case 0x9:
case 0xA:
case 0xE:
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
if (info.command() == 0x9 && info.basic.param2 == 0)
info.event = info.channel() | 0x80;
info.length = 0;
@@ -598,12 +598,12 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
case 0xF: // System Common, Meta or SysEx event
switch (info.event & 0x0F) {
case 0x2: // Song Position Pointer
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
break;
case 0x3: // Song Select
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
@@ -617,16 +617,16 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
break;
case 0x0: // SysEx
- info.length = readVLQ(_position._play_pos);
- info.ext.data = _position._play_pos;
- _position._play_pos += info.length;
+ info.length = readVLQ(_position._playPos);
+ info.ext.data = _position._playPos;
+ _position._playPos += info.length;
break;
case 0xF: // META event
- info.ext.type = *(_position._play_pos++);
- info.length = readVLQ(_position._play_pos);
- info.ext.data = _position._play_pos;
- _position._play_pos += info.length;
+ info.ext.type = *(_position._playPos++);
+ info.length = readVLQ(_position._playPos);
+ info.ext.data = _position._playPos;
+ _position._playPos += info.length;
if (info.ext.type == 0x2F) {// end of track reached
if (_pSnd->loop)
_pSnd->loop--;
@@ -677,21 +677,21 @@ void MidiParser_SCI::allNotesOff() {
// Turn off all active notes
for (i = 0; i < 128; ++i) {
for (j = 0; j < 16; ++j) {
- if ((_active_notes[i] & (1 << j)) && (_channelRemap[j] != -1)){
+ if ((_activeNotes[i] & (1 << j)) && (_channelRemap[j] != -1)){
sendToDriver(0x80 | j, i, 0);
}
}
}
// Turn off all hanging notes
- for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) {
- byte midiChannel = _hanging_notes[i].channel;
- if ((_hanging_notes[i].time_left) && (_channelRemap[midiChannel] != -1)) {
- sendToDriver(0x80 | midiChannel, _hanging_notes[i].note, 0);
- _hanging_notes[i].time_left = 0;
+ for (i = 0; i < ARRAYSIZE(_hangingNotes); i++) {
+ byte midiChannel = _hangingNotes[i].channel;
+ if ((_hangingNotes[i].timeLeft) && (_channelRemap[midiChannel] != -1)) {
+ sendToDriver(0x80 | midiChannel, _hangingNotes[i].note, 0);
+ _hangingNotes[i].timeLeft = 0;
}
}
- _hanging_notes_count = 0;
+ _hangingNotesCount = 0;
// To be sure, send an "All Note Off" event (but not all MIDI devices
// support this...).
@@ -703,7 +703,7 @@ void MidiParser_SCI::allNotesOff() {
}
}
- memset(_active_notes, 0, sizeof(_active_notes));
+ memset(_activeNotes, 0, sizeof(_activeNotes));
}
void MidiParser_SCI::setMasterVolume(byte masterVolume) {
diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h
index 82f34070a4..d3fd337644 100644
--- a/engines/sci/sound/midiparser_sci.h
+++ b/engines/sci/sound/midiparser_sci.h
@@ -65,7 +65,7 @@ public:
void setMasterVolume(byte masterVolume);
void setVolume(byte volume);
void stop() {
- _abort_parse = true;
+ _abortParse = true;
allNotesOff();
}
void pause() {
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 918b045cb9..a8a65d2aa4 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -125,7 +125,13 @@ void SciMusic::init() {
_pMidiDrv->setTimerCallback(this, &miditimerCallback);
_dwTempo = _pMidiDrv->getBaseTempo();
} else {
- error("Failed to initialize sound driver");
+ if (g_sci->getGameId() == GID_FUNSEEKER) {
+ // HACK: The Fun Seeker's Guide demo doesn't have patch 3 and the version
+ // of the Adlib driver (adl.drv) that it includes is unsupported. That demo
+ // doesn't have any sound anyway, so this shouldn't be fatal.
+ } else {
+ error("Failed to initialize sound driver");
+ }
}
// Find out what the first possible channel is (used, when doing channel
diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp
index 608c77136f..0337a8d306 100644
--- a/engines/sci/video/robot_decoder.cpp
+++ b/engines/sci/video/robot_decoder.cpp
@@ -109,13 +109,13 @@ bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) {
}
bool RobotDecoder::load(GuiResourceId id) {
- // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) -
+ // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) -
// its drawn at odd coordinates. SV can't play it either (along with some
// others), so it must be some new functionality added in RAMA's robot
// videos. Skip it for now.
if (g_sci->getGameId() == GID_RAMA && id == 1003)
return false;
-
+
// TODO: The robot video in the Lighthouse demo gets stuck
if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16)
return false;
@@ -247,7 +247,7 @@ void RobotDecoder::readNextPacket() {
audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2);
} else {
_fileStream->skip(audioChunkSize);
- }
+ }
}
void RobotDecoder::readHeaderChunk() {
diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h
index ebc3262939..437954f7fb 100644
--- a/engines/sci/video/robot_decoder.h
+++ b/engines/sci/video/robot_decoder.h
@@ -45,13 +45,13 @@ public:
bool loadStream(Common::SeekableReadStream *stream);
bool load(GuiResourceId id);
void close();
-
+
void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); }
Common::Point getPos() const { return _pos; }
protected:
void readNextPacket();
-
+
private:
class RobotVideoTrack : public FixedRateVideoTrack {
public:
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index b8722b6963..0c375efcdd 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -573,19 +573,19 @@ bool Actor_v2::checkWalkboxesHaveDirectPath(Common::Point &foundPath) {
return false;
}
-bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End,
- const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result)
+bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End,
+ const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result)
{
const Common::Point v1 = line1End - line1Start; // line1(n1) = line1Start + n1 * v1
const Common::Point v2 = line2End - line2Start; // line2(n2) = line2Start + n2 * v2
-
+
double det = v2.x * v1.y - v1.x * v2.y;
if (det == 0)
return false;
- double n1 = ((double)v2.x * (line2Start.y - line1Start.y) -
+ double n1 = ((double)v2.x * (line2Start.y - line1Start.y) -
(double)v2.y * (line2Start.x - line1Start.x)) / det;
- double n2 = ((double)v1.x * (line2Start.y - line1Start.y) -
+ double n2 = ((double)v1.x * (line2Start.y - line1Start.y) -
(double)v1.y * (line2Start.x - line1Start.x)) / det;
// both coefficients have to be in [0, 1], otherwise the intersection is
@@ -599,16 +599,16 @@ bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Comm
}
/*
- * MM v0 allows the actor to walk in a direct line between boxes to the target
+ * MM v0 allows the actor to walk in a direct line between boxes to the target
* if actor and target share a horizontal or vertical corridor.
- * If such a corridor is found the actor is not forced to go horizontally or
+ * If such a corridor is found the actor is not forced to go horizontally or
* vertically from one box to the next but can also walk diagonally.
*
- * Note: the original v0 interpreter sets the target destination for diagonal
+ * Note: the original v0 interpreter sets the target destination for diagonal
* walking only once and then rechecks whenever the actor reaches a new box if the
- * walk destination is still suitable for the current box.
- * ScummVM does not perform such a check, so it is possible to leave the walkboxes
- * in some cases, for example L-shaped rooms like the swimming pool (actor walks over water)
+ * walk destination is still suitable for the current box.
+ * ScummVM does not perform such a check, so it is possible to leave the walkboxes
+ * in some cases, for example L-shaped rooms like the swimming pool (actor walks over water)
* or the medical room (actor walks over examination table).
* To solve this we intersect the new walk destination with the actor's walkbox borders,
* so a recheck is done when the actor leaves his box. This is done by the
@@ -992,7 +992,7 @@ void Actor_v0::setDirection(int direction) {
res = 7; // Face Camera
break;
}
-
+
_animFrameRepeat = -1;
animateActor(res);
if (_moving)
@@ -1408,7 +1408,7 @@ void Actor::showActor() {
if (_vm->_game.version == 0) {
Actor_v0 *a = ((Actor_v0 *)this);
-
+
a->_costCommand = a->_costCommandNew = 0xFF;
for (int i = 0; i < 8; ++i) {
@@ -2056,7 +2056,7 @@ void Actor_v0::animateCostume() {
void Actor_v0::speakCheck() {
if (v0ActorTalkArray[_number] & 0x80)
return;
-
+
int cmd = newDirToOldDir(_facing);
if (_speaking & 0x80)
@@ -2884,7 +2884,7 @@ void Actor_v0::animateActor(int anim) {
_costCommandNew = anim;
_vm->_costumeLoader->costumeDecodeData(this, 0, 0);
-
+
if (dir == -1)
return;
diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h
index 0ed239d005..a733fdb4ed 100644
--- a/engines/scumm/actor.h
+++ b/engines/scumm/actor.h
@@ -377,7 +377,7 @@ public:
virtual void saveLoadWithSerializer(Serializer *ser);
protected:
- bool intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End,
+ bool intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End,
const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result);
virtual bool checkWalkboxesHaveDirectPath(Common::Point &foundPath);
};
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 4064853b6b..9ae75b6683 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -69,7 +69,7 @@ void ScummEngine::loadCJKFont() {
_cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode);
_2byteWidth = _2byteHeight = 12;
- _useCJKMode = true;
+ _useCJKMode = true;
#endif
} else if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) {
int numChar = 1413;
@@ -499,7 +499,7 @@ int CharsetRendererV3::getCharWidth(uint16 chr) {
if (_vm->_useCJKMode && (chr & 0x80))
spacing = _vm->_2byteWidth / 2;
-
+
if (!spacing)
spacing = *(_widthTable + chr);
@@ -644,7 +644,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
drawBits1(*vs, _left + vs->xstart, drawTop, charPtr, drawTop, origWidth, origHeight);
else
drawBits1(_vm->_textSurface, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, charPtr, drawTop, origWidth, origHeight);
-
+
if (is2byte) {
origWidth /= _vm->_textSurfaceMultiplier;
height /= _vm->_textSurfaceMultiplier;
@@ -1399,7 +1399,7 @@ void CharsetRendererTownsClassic::drawBitsN(const Graphics::Surface&, byte *dst,
_vm->_cjkFont->drawChar(_vm->_textSurface, _sjisCurChar, _left * _vm->_textSurfaceMultiplier, (_top - _vm->_screenTop) * _vm->_textSurfaceMultiplier, _vm->_townsCharsetColorMap[1], _shadowColor);
return;
}
-
+
bool scale2x = (_vm->_textSurfaceMultiplier == 2);
dst = (byte *)_vm->_textSurface.pixels + (_top - _vm->_screenTop) * _vm->_textSurface.pitch * _vm->_textSurfaceMultiplier + _left * _vm->_textSurfaceMultiplier;
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index b8f1d84045..1c1df51921 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -112,7 +112,7 @@ public:
class CharsetRendererClassic : public CharsetRendererCommon {
protected:
virtual void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height);
- void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask);
+ void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask);
virtual bool prepareDraw(uint16 chr);
int _width, _height, _origWidth, _origHeight;
@@ -195,7 +195,7 @@ public:
int getCharWidth(uint16 chr);
int getFontHeight();
-
+
private:
void enableShadow(bool enable);
void drawBits1(Graphics::Surface &dest, int x, int y, const byte *src, int drawTop, int width, int height);
diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp
index 6e7e9ff688..4ebdd00fdc 100644
--- a/engines/scumm/costume.cpp
+++ b/engines/scumm/costume.cpp
@@ -592,7 +592,7 @@ void ClassicCostumeRenderer::proc3_ami(Codec1 &v1) {
} while (1);
}
-void PCESetCostumeData(byte block[16][16], int index, byte value) {
+static void PCESetCostumeData(byte block[16][16], int index, byte value) {
int row = (index % 16);
int plane = (index / 16) % 4;
int colOffset = (index < 64) ? 8 : 0;
@@ -1188,7 +1188,7 @@ byte V0CostumeRenderer::drawLimb(const Actor *a, int limb) {
_draw_top = 200;
_draw_bottom = 0;
}
-
+
// Invalid current position?
if (a->_cost.curpos[limb] == 0xFFFF)
return 0;
@@ -1377,7 +1377,7 @@ byte V0CostumeLoader::increaseAnim(Actor *a, int limb) {
// Reset the comstume command
a0->_costCommandNew = 0xFF;
a0->_costCommand = 0xFF;
-
+
// Set the frame/start to invalid
a0->_cost.frame[limb] = 0xFFFF;
a0->_cost.start[limb] = 0xFFFF;
diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp
index 88681898f5..269ae9e10a 100644
--- a/engines/scumm/cursor.cpp
+++ b/engines/scumm/cursor.cpp
@@ -180,7 +180,7 @@ void ScummEngine_v70he::setDefaultCursor() {
0xff, 0xff, 0xff,
0, 0, 0, };
-
+
memset(_grabbedCursor, 5, sizeof(_grabbedCursor));
_cursor.hotspotX = _cursor.hotspotY = 2;
diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp
index edcf2e6fea..dc5acbdb7d 100644
--- a/engines/scumm/debugger.cpp
+++ b/engines/scumm/debugger.cpp
@@ -382,7 +382,7 @@ bool ScummDebugger::Cmd_Actor(int argc, const char **argv) {
DebugPrintf("Actor[%d].costume = %d\n", actnum, a->_costume);
}
} else if (!strcmp(argv[2], "name")) {
- DebugPrintf("Name of actor %d: %s\n", actnum,
+ DebugPrintf("Name of actor %d: %s\n", actnum,
_vm->getObjOrActorName(_vm->actorToObj(actnum)));
} else if (!strcmp(argv[2], "condmask")) {
if (argc > 3) {
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 5404c7f8b1..e5c3023380 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -242,6 +242,10 @@ static Common::String generateFilenameForDetection(const char *pattern, Filename
return result;
}
+bool ScummEngine::isMacM68kIMuse() const {
+ return _game.platform == Common::kPlatformMacintosh && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && !(_game.features & GF_MAC_CONTAINER);
+}
+
struct DetectorDesc {
Common::FSNode node;
Common::String md5;
@@ -473,6 +477,11 @@ static void computeGameSettingsFromMD5(const Common::FSList &fslist, const GameF
if (dr.language == UNK_LANG) {
dr.language = detectLanguage(fslist, dr.game.id);
}
+
+ // HACK: Detect between 68k and PPC versions
+ if (dr.game.platform == Common::kPlatformMacintosh && dr.game.version >= 5 && dr.game.heversion == 0 && strstr(gfp->pattern, "Data"))
+ dr.game.features |= GF_MAC_CONTAINER;
+
break;
}
}
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index be1b90e356..3120017db6 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -236,7 +236,7 @@ static const GameSettings gameVariantsTable[] = {
{"monkey", "VGA", "vga", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO1(GUIO_NOSPEECH)},
{"monkey", "EGA", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, GF_16COLOR, Common::kPlatformPC, GUIO1(GUIO_NOSPEECH)},
{"monkey", "No AdLib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR, GF_16COLOR, Common::kPlatformAtariST, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)},
- {"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)},
+ {"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)},
{"monkey", "CD", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)},
{"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)},
{"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)},
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 0e531daf73..945c5b6611 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -180,7 +180,7 @@ static const ResString string_map_table_v345[] = {
// "Moechten Sie wirklich neu starten? (J/N)J"
// Will react to J as 'Yes'
{5, _s("Are you sure you want to restart? (Y/N)")},
- // I18N: you may specify 'Yes' symbol at the end of the line. See previous comment
+ // I18N: you may specify 'Yes' symbol at the end of the line. See previous comment
{6, _s("Are you sure you want to quit? (Y/N)")},
// Added in SCUMM4
diff --git a/engines/scumm/he/logic/soccer.cpp b/engines/scumm/he/logic/soccer.cpp
index 05f377a736..2b29f93173 100644
--- a/engines/scumm/he/logic/soccer.cpp
+++ b/engines/scumm/he/logic/soccer.cpp
@@ -303,7 +303,7 @@ int LogicHEsoccer::op_1008(int outArray, int srcX, int srcY, int srcZ, int vecX,
putInArray(outArray, segmentsSoFar, 5, vecX);
putInArray(outArray, segmentsSoFar, 6, vecY);
putInArray(outArray, segmentsSoFar++, 7, vecZ);
- }
+ }
} else {
srcY = 0;
int thisVecX = vecX;
@@ -628,7 +628,7 @@ int LogicHEsoccer::op_1014(int32 srcX, int32 srcY, int32 srcZ, int32 velX, int32
adjustedVelZ = ((double)srcZ - 3869.0) / 100.0;
break;
}
-
+
int foundCollision = 0;
// work out which collision objects we might collide with (if any)
diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp
index 47b4b8ad33..798f703db6 100644
--- a/engines/scumm/he/wiz_he.cpp
+++ b/engines/scumm/he/wiz_he.cpp
@@ -2286,8 +2286,7 @@ void Wiz::fillWizLine(const WizParameters *params) {
lineP.depth = bitDepth;
if (params->processFlags & kWPFParams) {
- assert (params->params2 == 1); // Catch untested usage
- Graphics::drawThickLine(x1, y1, x2, y2, params->params1, color, drawProc, &lineP);
+ Graphics::drawThickLine(x1, y1, x2, y2, params->params1, params->params2, color, drawProc, &lineP);
} else {
Graphics::drawLine(x1, y1, x2, y2, color, drawProc, &lineP);
}
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index 27a72c2afe..016ba89e7b 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -164,7 +164,7 @@ bool IMuseInternal::isMT32(int sound) {
return true;
case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
- return true;
+ return false;
case MKTAG('G', 'M', 'D', ' '):
return false;
@@ -226,6 +226,45 @@ bool IMuseInternal::isMIDI(int sound) {
return false;
}
+bool IMuseInternal::supportsPercussion(int sound) {
+ byte *ptr = g_scumm->_res->_types[rtSound][sound]._address;
+ if (ptr == NULL)
+ return false;
+
+ uint32 tag = READ_BE_UINT32(ptr);
+ switch (tag) {
+ case MKTAG('A', 'D', 'L', ' '):
+ case MKTAG('A', 'S', 'F', 'X'): // Special AD class for old AdLib sound effects
+ case MKTAG('S', 'P', 'K', ' '):
+ return false;
+
+ case MKTAG('A', 'M', 'I', ' '):
+ case MKTAG('R', 'O', 'L', ' '):
+ return true;
+
+ case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
+ // This is MIDI, i.e. uses MIDI style program changes, but without a
+ // special percussion channel.
+ return false;
+
+ case MKTAG('G', 'M', 'D', ' '):
+ case MKTAG('M', 'I', 'D', 'I'): // Occurs in Sam & Max
+ return true;
+ }
+
+ // Old style 'RO' has equivalent properties to 'ROL'
+ if (ptr[0] == 'R' && ptr[1] == 'O')
+ return true;
+ // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
+ // FIXME: Right now we're pretending it's GM.
+ if (ptr[4] == 'S' && ptr[5] == 'O')
+ return true;
+
+ error("Unknown music type: '%c%c%c%c'", (char)tag >> 24, (char)tag >> 16, (char)tag >> 8, (char)tag);
+
+ return false;
+}
+
MidiDriver *IMuseInternal::getBestMidiDriver(int sound) {
MidiDriver *driver = NULL;
diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index 3b0d36e119..846e2d7545 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -204,6 +204,7 @@ protected:
bool _isMT32;
bool _isMIDI;
+ bool _supportsPercussion;
protected:
// Player part
@@ -458,6 +459,7 @@ protected:
byte *findStartOfSound(int sound, int ct = (kMThd | kFORM));
bool isMT32(int sound);
bool isMIDI(int sound);
+ bool supportsPercussion(int sound);
int get_queue_sound_status(int sound) const;
void handle_marker(uint id, byte data);
int get_channel_volume(uint a);
diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index 73e7704469..89c16a8bb5 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -27,6 +27,7 @@
#include "common/util.h"
#include "scumm/imuse/imuse_internal.h"
#include "scumm/saveload.h"
+#include "scumm/scumm.h"
namespace Scumm {
@@ -365,7 +366,17 @@ void Part::set_instrument(uint b) {
_bank = (byte)(b >> 8);
if (_bank)
error("Non-zero instrument bank selection. Please report this");
- _instrument.program((byte)b, _player->isMT32());
+ // HACK: Horrible hack to allow tracing of program change source.
+ // The Mac m68k versions of MI2 and Indy4 use a different program "bank"
+ // when it gets program change events through the iMuse SysEx handler.
+ // We emulate this by introducing a special instrument, which sets
+ // the instrument via sysEx_customInstrument. This seems to be
+ // exclusively used for special sound effects like the "spit" sound.
+ if (g_scumm->isMacM68kIMuse()) {
+ _instrument.macSfx(b);
+ } else {
+ _instrument.program((byte)b, _player->isMT32());
+ }
if (clearToTransmit())
_instrument.send(_mc);
}
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index 53ccfb3734..3a9c42a920 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -78,6 +78,7 @@ Player::Player() :
_speed(128),
_isMT32(false),
_isMIDI(false),
+ _supportsPercussion(false),
_se(0),
_vol_chan(0) {
}
@@ -103,6 +104,7 @@ bool Player::startSound(int sound, MidiDriver *midi) {
_isMT32 = _se->isMT32(sound);
_isMIDI = _se->isMIDI(sound);
+ _supportsPercussion = _se->supportsPercussion(sound);
_parts = NULL;
_active = true;
@@ -386,6 +388,8 @@ void Player::sysEx(const byte *p, uint16 len) {
// SysEx manufacturer 0x97 has been spotted in the
// Monkey Island 2 AdLib music, so don't make this a
// fatal error. See bug #1481383.
+ // The Macintosh version of Monkey Island 2 simply
+ // ignores these SysEx events too.
if (a == 0)
warning("Unknown SysEx manufacturer 0x00 0x%02X 0x%02X", p[0], p[1]);
else
@@ -1009,6 +1013,7 @@ void Player::fixAfterLoad() {
_parser->jumpToTick(_music_tick); // start_seq_sound already switched tracks
_isMT32 = _se->isMT32(_id);
_isMIDI = _se->isMIDI(_id);
+ _supportsPercussion = _se->supportsPercussion(_id);
}
}
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index 11bb4e7605..61c73b1e2d 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -278,6 +278,21 @@ private:
byte _instrument[23];
};
+class Instrument_MacSfx : public InstrumentInternal {
+private:
+ byte _program;
+
+public:
+ Instrument_MacSfx(byte program);
+ Instrument_MacSfx(Serializer *s);
+ void saveOrLoad(Serializer *s);
+ void send(MidiChannel *mc);
+ void copy_to(Instrument *dest) { dest->macSfx(_program); }
+ bool is_valid() {
+ return (_program < 128);
+ }
+};
+
////////////////////////////////////////
//
// Instrument class members
@@ -326,6 +341,14 @@ void Instrument::pcspk(const byte *instrument) {
_instrument = new Instrument_PcSpk(instrument);
}
+void Instrument::macSfx(byte prog) {
+ clear();
+ if (prog > 127)
+ return;
+ _type = itMacSfx;
+ _instrument = new Instrument_MacSfx(prog);
+}
+
void Instrument::saveOrLoad(Serializer *s) {
if (s->isSaving()) {
s->saveByte(_type);
@@ -349,6 +372,9 @@ void Instrument::saveOrLoad(Serializer *s) {
case itPcSpk:
_instrument = new Instrument_PcSpk(s);
break;
+ case itMacSfx:
+ _instrument = new Instrument_MacSfx(s);
+ break;
default:
warning("No known instrument classification #%d", (int)_type);
_type = itNone;
@@ -528,4 +554,38 @@ void Instrument_PcSpk::send(MidiChannel *mc) {
mc->sysEx_customInstrument('SPK ', (byte *)&_instrument);
}
+////////////////////////////////////////
+//
+// Instrument_MacSfx class members
+//
+////////////////////////////////////////
+
+Instrument_MacSfx::Instrument_MacSfx(byte program) :
+ _program(program) {
+ if (program > 127) {
+ _program = 255;
+ }
+}
+
+Instrument_MacSfx::Instrument_MacSfx(Serializer *s) {
+ _program = 255;
+ if (!s->isSaving()) {
+ saveOrLoad(s);
+ }
+}
+
+void Instrument_MacSfx::saveOrLoad(Serializer *s) {
+ if (s->isSaving()) {
+ s->saveByte(_program);
+ } else {
+ _program = s->loadByte();
+ }
+}
+
+void Instrument_MacSfx::send(MidiChannel *mc) {
+ if (_program > 127) {
+ return;
+ }
+ mc->sysEx_customInstrument('MAC ', &_program);
+}
} // End of namespace Scumm
diff --git a/engines/scumm/imuse/instrument.h b/engines/scumm/imuse/instrument.h
index a855c64155..7e09e86fa5 100644
--- a/engines/scumm/imuse/instrument.h
+++ b/engines/scumm/imuse/instrument.h
@@ -52,7 +52,8 @@ public:
itProgram = 1,
itAdLib = 2,
itRoland = 3,
- itPcSpk = 4
+ itPcSpk = 4,
+ itMacSfx = 5
};
Instrument() : _type(0), _instrument(0) { }
@@ -72,6 +73,7 @@ public:
void adlib(const byte *instrument);
void roland(const byte *instrument);
void pcspk(const byte *instrument);
+ void macSfx(byte program);
byte getType() { return _type; }
bool isValid() { return (_instrument ? _instrument->is_valid() : false); }
diff --git a/engines/scumm/imuse/mac_m68k.cpp b/engines/scumm/imuse/mac_m68k.cpp
new file mode 100644
index 0000000000..0980ef1fd2
--- /dev/null
+++ b/engines/scumm/imuse/mac_m68k.cpp
@@ -0,0 +1,514 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "scumm/imuse/mac_m68k.h"
+
+#include "common/util.h"
+#include "common/macresman.h"
+#include "common/stream.h"
+
+namespace Scumm {
+
+MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer)
+ : MidiDriver_Emulated(mixer) {
+}
+
+MacM68kDriver::~MacM68kDriver() {
+}
+
+int MacM68kDriver::open() {
+ if (_isOpen) {
+ return MERR_ALREADY_OPEN;
+ }
+
+ const int error = MidiDriver_Emulated::open();
+ if (error) {
+ return error;
+ }
+
+ for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
+ _channels[i].init(this, i);
+ }
+
+ memset(_voiceChannels, 0, sizeof(_voiceChannels));
+ _lastUsedVoiceChannel = 0;
+
+ loadAllInstruments();
+
+ _pitchTable[116] = 1664510;
+ _pitchTable[117] = 1763487;
+ _pitchTable[118] = 1868350;
+ _pitchTable[119] = 1979447;
+ _pitchTable[120] = 2097152;
+ _pitchTable[121] = 2221855;
+ _pitchTable[122] = 2353973;
+ _pitchTable[123] = 2493948;
+ _pitchTable[124] = 2642246;
+ _pitchTable[125] = 2799362;
+ _pitchTable[126] = 2965820;
+ _pitchTable[127] = 3142177;
+ for (int i = 115; i >= 0; --i) {
+ _pitchTable[i] = _pitchTable[i + 12] / 2;
+ }
+
+ _volumeTable = new byte[8192];
+ for (int i = 0; i < 32; ++i) {
+ for (int j = 0; j < 256; ++j) {
+ _volumeTable[i * 256 + j] = ((-128 + j) * _volumeBaseTable[i]) / 127 - 128;
+ }
+ }
+
+ _mixBuffer = 0;
+ _mixBufferLength = 0;
+
+ // We set the output sound type to music here to allow sound volume
+ // adjustment. The drawback here is that we can not control the music and
+ // sfx separately here. But the AdLib output has the same issue so it
+ // should not be that bad.
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ return 0;
+}
+
+void MacM68kDriver::close() {
+ if (!_isOpen) {
+ return;
+ }
+
+ _mixer->stopHandle(_mixerSoundHandle);
+ _isOpen = false;
+ for (InstrumentMap::iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
+ delete[] i->_value.data;
+ }
+ _instruments.clear();
+ delete[] _volumeTable;
+ _volumeTable = 0;
+ delete[] _mixBuffer;
+ _mixBuffer = 0;
+ _mixBufferLength = 0;
+}
+
+void MacM68kDriver::send(uint32 d) {
+ assert(false);
+}
+
+void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
+ assert(false);
+}
+
+MidiChannel *MacM68kDriver::allocateChannel() {
+ for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
+ if (_channels[i].allocate()) {
+ return &_channels[i];
+ }
+ }
+
+ return 0;
+}
+
+MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const {
+ InstrumentMap::const_iterator i = _instruments.find(idx);
+ if (i != _instruments.end()) {
+ return i->_value;
+ } else {
+ return _defaultInstrument;
+ }
+}
+
+void MacM68kDriver::generateSamples(int16 *buf, int len) {
+ int silentChannels = 0;
+
+ if (_mixBufferLength < len) {
+ delete[] _mixBuffer;
+
+ _mixBufferLength = len;
+ _mixBuffer = new int[_mixBufferLength];
+ assert(_mixBuffer);
+ }
+ memset(_mixBuffer, 0, sizeof(int) * _mixBufferLength);
+
+ for (int i = 0; i < kChannelCount; ++i) {
+ OutputChannel &out = _voiceChannels[i].out;
+ if (out.isFinished) {
+ ++silentChannels;
+ continue;
+ }
+
+ byte *volumeTable = &_volumeTable[(out.volume / 4) * 256];
+ int *buffer = _mixBuffer;
+
+ int samplesLeft = len;
+ while (samplesLeft) {
+ out.subPos += out.pitchModifier;
+ while (out.subPos >= 0x10000) {
+ out.subPos -= 0x10000;
+ out.instrument++;
+ }
+
+ if (out.instrument >= out.end) {
+ if (!out.start) {
+ break;
+ }
+
+ out.instrument = out.start;
+ out.subPos = 0;
+ }
+
+ *buffer++ += volumeTable[*out.instrument];
+ --samplesLeft;
+ }
+
+ if (samplesLeft) {
+ out.isFinished = true;
+ while (samplesLeft--) {
+ *buffer++ += 0x80;
+ }
+ }
+ }
+
+ const int *buffer = _mixBuffer;
+ const int silenceAdd = silentChannels << 7;
+ while (len--) {
+ *buf++ = (((*buffer++ + silenceAdd) >> 3) << 8) ^ 0x8000;
+ }
+}
+
+void MacM68kDriver::loadAllInstruments() {
+ Common::MacResManager resource;
+ if (resource.open("iMUSE Setups")) {
+ if (!resource.hasResFork()) {
+ error("MacM68kDriver::loadAllInstruments: \"iMUSE Setups\" loaded, but no resource fork present");
+ }
+
+ for (int i = 0x3E7; i < 0x468; ++i) {
+ Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i);
+ if (stream) {
+ addInstrument(i, stream);
+ delete stream;
+ }
+ }
+
+ for (int i = 0x7D0; i < 0x8D0; ++i) {
+ Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i);
+ if (stream) {
+ addInstrument(i, stream);
+ delete stream;
+ }
+ }
+
+ InstrumentMap::iterator inst = _instruments.find(kDefaultInstrument);
+ if (inst != _instruments.end()) {
+ _defaultInstrument = inst->_value;
+ } else {
+ error("MacM68kDriver::loadAllInstruments: Could not load default instrument");
+ }
+ } else {
+ error("MacM68kDriver::loadAllInstruments: Could not load \"iMUSE Setups\"");
+ }
+}
+
+void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) {
+ // We parse the "SND" files manually here, since we need special data
+ // from their header and need to work on them raw while mixing.
+ data->skip(2);
+ int count = data->readUint16BE();
+ data->skip(2 * (3 * count));
+ count = data->readUint16BE();
+ data->skip(2 * (4 * count));
+
+ Instrument inst;
+ // Skip (optional) pointer to data
+ data->skip(4);
+ inst.length = data->readUint32BE();
+ inst.sampleRate = data->readUint32BE();
+ inst.loopStart = data->readUint32BE();
+ inst.loopEnd = data->readUint32BE();
+ // Skip encoding
+ data->skip(1);
+ inst.baseFrequency = data->readByte();
+
+ inst.data = new byte[inst.length];
+ assert(inst.data);
+ data->read(inst.data, inst.length);
+ _instruments[idx] = inst;
+}
+
+void MacM68kDriver::setPitch(OutputChannel *out, int frequency) {
+ out->frequency = frequency;
+ out->isFinished = false;
+
+ const int pitchIdx = (frequency >> 7) + 60 - out->baseFrequency;
+ assert(pitchIdx >= 0);
+
+ const int low7Bits = frequency & 0x7F;
+ if (low7Bits) {
+ out->pitchModifier = _pitchTable[pitchIdx] + (((_pitchTable[pitchIdx + 1] - _pitchTable[pitchIdx]) * low7Bits) >> 7);
+ } else {
+ out->pitchModifier = _pitchTable[pitchIdx];
+ }
+}
+
+void MacM68kDriver::VoiceChannel::off() {
+ if (out.start) {
+ out.isFinished = true;
+ }
+
+ part->removeVoice(this);
+ part = 0;
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::release() {
+ _allocated = false;
+ while (_voice) {
+ _voice->off();
+ }
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) {
+ uint8 type = b & 0xF0;
+ uint8 p1 = (b >> 8) & 0xFF;
+ uint8 p2 = (b >> 16) & 0xFF;
+
+ switch (type) {
+ case 0x80:
+ noteOff(p1);
+ break;
+
+ case 0x90:
+ if (p2) {
+ noteOn(p1, p2);
+ } else {
+ noteOff(p1);
+ }
+ break;
+
+ case 0xB0:
+ controlChange(p1, p2);
+ break;
+
+ case 0xE0:
+ pitchBend((p1 | (p2 << 7)) - 0x2000);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) {
+ for (VoiceChannel *i = _voice; i; i = i->next) {
+ if (i->note == note) {
+ if (_sustain) {
+ i->sustainNoteOff = true;
+ } else {
+ i->off();
+ }
+ }
+ }
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) {
+ // Do not start a not unless there is an instrument set up
+ if (!_instrument.data) {
+ return;
+ }
+
+ // Allocate a voice channel
+ VoiceChannel *voice = _owner->allocateVoice(_priority);
+ if (!voice) {
+ return;
+ }
+ addVoice(voice);
+
+ voice->note = note;
+ // This completly ignores the note's volume, but is in accordance
+ // to the original.
+ voice->out.volume = _volume;
+
+ // Set up the instrument data
+ voice->out.baseFrequency = _instrument.baseFrequency;
+ voice->out.soundStart = _instrument.data;
+ voice->out.soundEnd = _instrument.data + _instrument.length;
+ if (_instrument.loopEnd && _instrument.loopEnd - 12 > _instrument.loopStart) {
+ voice->out.loopStart = _instrument.data + _instrument.loopStart;
+ voice->out.loopEnd = _instrument.data + _instrument.loopEnd;
+ } else {
+ voice->out.loopStart = 0;
+ voice->out.loopEnd = voice->out.soundEnd;
+ }
+
+ voice->out.start = voice->out.loopStart;
+ voice->out.end = voice->out.loopEnd;
+
+ // Set up the pitch
+ _owner->setPitch(&voice->out, (note << 7) + _pitchBend);
+
+ // Set up the sample position
+ voice->out.instrument = voice->out.soundStart;
+ voice->out.subPos = 0;
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) {
+ _instrument = _owner->getInstrument(program + kProgramChangeBase);
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::pitchBend(int16 bend) {
+ _pitchBend = (bend * _pitchBendFactor) >> 6;
+ for (VoiceChannel *i = _voice; i; i = i->next) {
+ _owner->setPitch(&i->out, (i->note << 7) + _pitchBend);
+ }
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value) {
+ switch (control) {
+ // volume change
+ case 7:
+ _volume = value;
+ for (VoiceChannel *i = _voice; i; i = i->next) {
+ i->out.volume = value;
+ i->out.isFinished = false;
+ }
+ break;
+
+ // sustain
+ case 64:
+ _sustain = value;
+ if (!_sustain) {
+ for (VoiceChannel *i = _voice; i; i = i->next) {
+ if (i->sustainNoteOff) {
+ i->off();
+ }
+ }
+ }
+ break;
+
+ // all notes off
+ case 123:
+ for (VoiceChannel *i = _voice; i; i = i->next) {
+ i->off();
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) {
+ _pitchBendFactor = value;
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) {
+ _priority = value;
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) {
+ assert(instr);
+ if (type == 'MAC ') {
+ _instrument = _owner->getInstrument(*instr + kSysExBase);
+ }
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::init(MacM68kDriver *owner, byte channel) {
+ _owner = owner;
+ _number = channel;
+ _allocated = false;
+}
+
+bool MacM68kDriver::MidiChannel_MacM68k::allocate() {
+ if (_allocated) {
+ return false;
+ }
+
+ _allocated = true;
+ _voice = 0;
+ _priority = 0;
+ memset(&_instrument, 0, sizeof(_instrument));
+ _pitchBend = 0;
+ _pitchBendFactor = 0;
+ _volume = 0;
+ return true;
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
+ voice->next = _voice;
+ voice->prev = 0;
+ voice->part = this;
+ if (_voice) {
+ _voice->prev = voice;
+ }
+ _voice = voice;
+}
+
+void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) {
+ VoiceChannel *i = _voice;
+ while (i && i != voice) {
+ i = i->next;
+ }
+
+ if (i) {
+ if (i->next) {
+ i->next->prev = i->prev;
+ }
+
+ if (i->prev) {
+ i->prev->next = i->next;
+ } else {
+ _voice = i->next;
+ }
+ }
+}
+
+MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) {
+ VoiceChannel *channel = 0;
+ for (int i = 0; i < kChannelCount; ++i) {
+ if (++_lastUsedVoiceChannel == kChannelCount) {
+ _lastUsedVoiceChannel = 0;
+ }
+
+ VoiceChannel *cur = &_voiceChannels[_lastUsedVoiceChannel];
+ if (!cur->part) {
+ memset(cur, 0, sizeof(*cur));
+ return cur;
+ } else if (!cur->next) {
+ if (cur->part->_priority <= priority) {
+ priority = cur->part->_priority;
+ channel = cur;
+ }
+ }
+ }
+
+ if (channel) {
+ channel->off();
+ memset(channel, 0, sizeof(*channel));
+ }
+
+ return channel;
+}
+
+const int MacM68kDriver::_volumeBaseTable[32] = {
+ 0, 0, 1, 1, 2, 3, 5, 6,
+ 8, 11, 13, 16, 19, 22, 26, 30,
+ 34, 38, 43, 48, 53, 58, 64, 70,
+ 76, 83, 89, 96, 104, 111, 119, 127
+};
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse/mac_m68k.h b/engines/scumm/imuse/mac_m68k.h
new file mode 100644
index 0000000000..59e2f68b9b
--- /dev/null
+++ b/engines/scumm/imuse/mac_m68k.h
@@ -0,0 +1,177 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SCUMM_IMUSE_MAC_M68K_H
+#define SCUMM_IMUSE_MAC_M68K_H
+
+#include "audio/softsynth/emumidi.h"
+
+#include "common/hashmap.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Scumm {
+
+class MacM68kDriver : public MidiDriver_Emulated {
+ friend class MidiChannel_MacM68k;
+public:
+ MacM68kDriver(Audio::Mixer *mixer);
+ ~MacM68kDriver();
+
+ virtual int open();
+ virtual void close();
+
+ virtual void send(uint32 d);
+ virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
+
+ virtual MidiChannel *allocateChannel();
+ virtual MidiChannel *getPercussionChannel() { return 0; }
+
+ virtual bool isStereo() const { return false; }
+ virtual int getRate() const {
+ // The original is using a frequency of approx. 22254.54546 here.
+ // To be precise it uses the 16.16 fixed point value 0x56EE8BA3.
+ return 22254;
+ }
+
+protected:
+ virtual void generateSamples(int16 *buf, int len);
+ virtual void onTimer() {}
+
+private:
+ int *_mixBuffer;
+ int _mixBufferLength;
+
+ struct Instrument {
+ uint length;
+ uint sampleRate;
+ uint loopStart;
+ uint loopEnd;
+ int baseFrequency;
+
+ byte *data;
+ };
+
+ enum {
+ kDefaultInstrument = 0x3E7,
+ kProgramChangeBase = 0x3E8,
+ kSysExBase = 0x7D0
+ };
+
+ Instrument getInstrument(int idx) const;
+ typedef Common::HashMap<int, Instrument> InstrumentMap;
+ InstrumentMap _instruments;
+ Instrument _defaultInstrument;
+ void loadAllInstruments();
+ void addInstrument(int idx, Common::SeekableReadStream *data);
+
+ struct OutputChannel {
+ int pitchModifier;
+
+ const byte *instrument;
+ uint subPos;
+
+ const byte *start;
+ const byte *end;
+
+ const byte *soundStart;
+ const byte *soundEnd;
+ const byte *loopStart;
+ const byte *loopEnd;
+
+ int frequency;
+ int volume;
+
+ bool isFinished;
+
+ int baseFrequency;
+ };
+
+ void setPitch(OutputChannel *out, int frequency);
+ int _pitchTable[128];
+
+ byte *_volumeTable;
+ static const int _volumeBaseTable[32];
+
+ class MidiChannel_MacM68k;
+
+ struct VoiceChannel {
+ MidiChannel_MacM68k *part;
+ VoiceChannel *prev, *next;
+ int channel;
+ int note;
+ bool sustainNoteOff;
+ OutputChannel out;
+
+ void off();
+ };
+
+ class MidiChannel_MacM68k : public MidiChannel {
+ friend class MacM68kDriver;
+ public:
+ virtual MidiDriver *device() { return _owner; }
+ virtual byte getNumber() { return _number; }
+ virtual void release();
+
+ virtual void send(uint32 b);
+ virtual void noteOff(byte note);
+ virtual void noteOn(byte note, byte velocity);
+ virtual void programChange(byte program);
+ virtual void pitchBend(int16 bend);
+ virtual void controlChange(byte control, byte value);
+ virtual void pitchBendFactor(byte value);
+ virtual void priority(byte value);
+ virtual void sysEx_customInstrument(uint32 type, const byte *instr);
+
+ void init(MacM68kDriver *owner, byte channel);
+ bool allocate();
+
+ void addVoice(VoiceChannel *voice);
+ void removeVoice(VoiceChannel *voice);
+ private:
+ MacM68kDriver *_owner;
+ bool _allocated;
+ int _number;
+
+ VoiceChannel *_voice;
+ int _priority;
+ int _sustain;
+ Instrument _instrument;
+ int _pitchBend;
+ int _pitchBendFactor;
+ int _volume;
+ };
+
+ MidiChannel_MacM68k _channels[32];
+
+ enum {
+ kChannelCount = 8
+ };
+ VoiceChannel _voiceChannels[kChannelCount];
+ int _lastUsedVoiceChannel;
+ VoiceChannel *allocateVoice(int priority);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp
index 85ffc86f47..8f230ebac3 100644
--- a/engines/scumm/imuse/sysex_scumm.cpp
+++ b/engines/scumm/imuse/sysex_scumm.cpp
@@ -71,7 +71,7 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
part->set_pri(buf[2]);
part->volume(buf[3]);
part->set_pan(buf[4]);
- part->_percussion = player->_isMIDI ? ((buf[5] & 0x80) > 0) : false;
+ part->_percussion = player->_supportsPercussion ? ((buf[5] & 0x80) > 0) : false;
part->set_transpose(buf[5]);
part->set_detune(buf[6]);
part->pitchBendFactor(buf[7]);
diff --git a/engines/scumm/midiparser_ro.cpp b/engines/scumm/midiparser_ro.cpp
index 1a31d1ca82..8549a9262d 100644
--- a/engines/scumm/midiparser_ro.cpp
+++ b/engines/scumm/midiparser_ro.cpp
@@ -62,13 +62,13 @@ void MidiParser_RO::parseNextEvent (EventInfo &info) {
info.delta = 0;
do {
- info.start = _position._play_pos;
- info.event = *(_position._play_pos++);
+ info.start = _position._playPos;
+ info.event = *(_position._playPos++);
if (info.command() == 0xA) {
++_lastMarkerCount;
info.event = 0xF0;
} else if (info.event == 0xF0 || info.event == 0xF1) {
- byte delay = *(_position._play_pos++);
+ byte delay = *(_position._playPos++);
info.delta += delay;
if (info.event == 0xF1) {
// This event is, as far as we have been able
@@ -95,16 +95,16 @@ void MidiParser_RO::parseNextEvent (EventInfo &info) {
if (info.event < 0x80)
return;
- _position._running_status = info.event;
+ _position._runningStatus = info.event;
switch (info.command()) {
case 0xC:
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
case 0x8: case 0x9: case 0xB:
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
if (info.command() == 0x9 && info.basic.param2 == 0)
info.event = info.channel() | 0x80;
info.length = 0;
@@ -133,7 +133,7 @@ bool MidiParser_RO::loadMusic (byte *data, uint32 size) {
return false;
}
- _num_tracks = 1;
+ _numTracks = 1;
_ppqn = 120;
_tracks[0] = pos + 2;
_markerCount = _lastMarkerCount = 0;
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 1f219f5187..8499c9bad3 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -27,6 +27,7 @@ MODULE_OBJS := \
imuse/imuse_part.o \
imuse/imuse_player.o \
imuse/instrument.o \
+ imuse/mac_m68k.o \
imuse/pcspk.o \
imuse/sysex_samnmax.o \
imuse/sysex_scumm.o \
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
index 399cd91324..77c75c4ad6 100644
--- a/engines/scumm/object.cpp
+++ b/engines/scumm/object.cpp
@@ -335,7 +335,7 @@ int ScummEngine::whereIsObject(int object) const {
return WIO_NOT_FOUND;
if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) &&
- _objectOwnerTable[object] != OF_OWNER_ROOM)
+ _objectOwnerTable[object] != OF_OWNER_ROOM)
{
for (i = 0; i < _numInventory; i++)
if (_inventory[i] == object)
@@ -1225,7 +1225,7 @@ byte *ScummEngine::getOBCDFromObject(int obj, bool v0CheckInventory) {
byte *ptr;
if ((_game.version != 0 || OBJECT_V0_TYPE(obj) == 0) &&
- _objectOwnerTable[obj] != OF_OWNER_ROOM)
+ _objectOwnerTable[obj] != OF_OWNER_ROOM)
{
if (_game.version == 0 && !v0CheckInventory)
return 0;
diff --git a/engines/scumm/player_apple2.cpp b/engines/scumm/player_apple2.cpp
index a8e150caa9..58e4f78a94 100644
--- a/engines/scumm/player_apple2.cpp
+++ b/engines/scumm/player_apple2.cpp
@@ -61,8 +61,8 @@ public:
private:
void _update(int interval /*a*/, int count /*y*/) { // D076
- assert(interval > 0); // 0 == 256?
- assert(count > 0); // 0 == 256?
+ assert(interval > 0); // 0 == 256?
+ assert(count > 0); // 0 == 256?
for (; count >= 0; --count) {
_player->speakerToggle();
@@ -99,7 +99,7 @@ public:
++_pos;
return false;
- }
+ }
return true;
}
@@ -112,7 +112,7 @@ private:
assert(interval > 0); // 0 == 256?
int a = (interval >> 3) + count;
- for (int y = a; y > 0; --y) {
+ for (int y = a; y > 0; --y) {
_player->generateSamples(1292 - 5*interval);
_player->speakerToggle();
@@ -206,7 +206,7 @@ private:
_bitmask1 = 0x3;
_bitmask2 = 0x3;
-
+
_updateInterval2 = param0;
if (_updateInterval2 == 0)
_bitmask2 = 0x0;
@@ -234,9 +234,9 @@ private:
if (_updateRemain2 == 0) {
_updateRemain2 = _updateInterval2;
- // use only first voice's data (bitmask1) if both voices are triggered
+ // use only first voice's data (bitmask1) if both voices are triggered
if (_updateRemain1 != 0) {
- _speakerShiftReg ^= _bitmask2;
+ _speakerShiftReg ^= _bitmask2;
}
}
@@ -256,7 +256,7 @@ private:
protected:
const byte *_params;
-
+
byte _updateRemain1;
byte _updateRemain2;
@@ -309,7 +309,7 @@ private:
for (int i = count; i > 0; --i) {
_player->generateSamples(10 + 5*interval);
_player->speakerToggle();
-
+
_player->generateSamples(5 + 5*interval);
_player->speakerToggle();
}
@@ -332,20 +332,20 @@ private:
// LD000[loc] ^ LD00A[loc]
const byte AppleII_SoundFunction5_Noise::_noiseTable[256] = {
- 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63,
- 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c,
- 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc,
- 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64,
+ 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63,
+ 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c,
+ 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc,
+ 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64,
0xe5, 0xb5, 0x5d, 0xe0, 0xb7, 0x7d, 0xe9, 0x8c, 0x55, 0x65, 0xc5, 0xb5, 0x5d, 0xd8, 0x09, 0x0d,
- 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3,
- 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a,
- 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d,
- 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c,
+ 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3,
+ 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a,
+ 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d,
+ 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c,
0x0a, 0x5d, 0x1d, 0x61, 0x10, 0x3c, 0x0b, 0x19, 0x88, 0x21, 0xc0, 0x21, 0x07, 0x00, 0x65, 0x62,
- 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06,
- 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d,
- 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48,
- 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37,
+ 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06,
+ 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d,
+ 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48,
+ 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37,
0x19, 0x37, 0x00, 0xf1, 0x00, 0x01, 0x1f, 0x00, 0xad, 0xc1, 0x01, 0x01, 0x2e, 0x00, 0x40, 0xc6,
0x7a, 0x9b, 0x95, 0x43, 0xfc, 0x18, 0xd2, 0x9e, 0x2a, 0x5a, 0x4b, 0x2a, 0xb6, 0x87, 0x30, 0x6c
};
@@ -394,20 +394,20 @@ void Player_AppleII::startSound(int nr) {
case 0: // empty (nothing to play)
resetState();
return;
- case 1:
- _soundFunc = new AppleII_SoundFunction1_FreqUpDown();
+ case 1:
+ _soundFunc = new AppleII_SoundFunction1_FreqUpDown();
break;
- case 2:
- _soundFunc = new AppleII_SoundFunction2_SymmetricWave();
+ case 2:
+ _soundFunc = new AppleII_SoundFunction2_SymmetricWave();
break;
- case 3:
- _soundFunc = new AppleII_SoundFunction3_AsymmetricWave();
+ case 3:
+ _soundFunc = new AppleII_SoundFunction3_AsymmetricWave();
break;
- case 4:
- _soundFunc = new AppleII_SoundFunction4_Polyphone();
+ case 4:
+ _soundFunc = new AppleII_SoundFunction4_Polyphone();
break;
- case 5:
- _soundFunc = new AppleII_SoundFunction5_Noise();
+ case 5:
+ _soundFunc = new AppleII_SoundFunction5_Noise();
break;
}
_soundFunc->init(this, _params);
@@ -484,7 +484,7 @@ int Player_AppleII::readBuffer(int16 *buffer, const int numSamples) {
// toggle speaker on/off
void Player_AppleII::speakerToggle() {
- _speakerState ^= 0x1;
+ _speakerState ^= 0x1;
}
void Player_AppleII::generateSamples(int cycles) {
@@ -492,8 +492,8 @@ void Player_AppleII::generateSamples(int cycles) {
}
void Player_AppleII::wait(int interval, int count /*y*/) {
- assert(count > 0); // 0 == 256?
- assert(interval > 0); // 0 == 256?
+ assert(count > 0); // 0 == 256?
+ assert(interval > 0); // 0 == 256?
generateSamples(11 + count*(8 + 5 * interval));
}
diff --git a/engines/scumm/player_apple2.h b/engines/scumm/player_apple2.h
index b4a7d409fb..e1ec9d8946 100644
--- a/engines/scumm/player_apple2.h
+++ b/engines/scumm/player_apple2.h
@@ -36,7 +36,7 @@ namespace Scumm {
class ScummEngine;
/*
- * Optimized for use with periodical read/write phases when the buffer
+ * Optimized for use with periodical read/write phases when the buffer
* is filled in a write phase and completely read in a read phase.
* The growing strategy is optimized for repeated small (e.g. 2 bytes)
* single writes resulting in large buffers
@@ -133,7 +133,7 @@ static const double APPLEII_CPU_CLOCK = 1020484.5; // ~ 1.02 MHz
/*
* Converts the 1-bit speaker state values into audio samples.
- * This is done by aggregation of the speaker states at each
+ * This is done by aggregation of the speaker states at each
* CPU cycle in a sampling period into an audio sample.
*/
class SampleConverter {
@@ -144,7 +144,7 @@ private:
}
public:
- SampleConverter() :
+ SampleConverter() :
_cyclesPerSampleFP(0),
_missingCyclesFP(0),
_sampleCyclesSumFP(0),
@@ -156,7 +156,7 @@ public:
void reset() {
_missingCyclesFP = 0;
_sampleCyclesSumFP = 0;
- _buffer.clear();
+ _buffer.clear();
}
uint32 availableSize() const {
@@ -245,7 +245,7 @@ public:
virtual void setMusicVolume(int vol) { _sampleConverter.setMusicVolume(vol); }
void setSampleRate(int rate) {
_sampleRate = rate;
- _sampleConverter.setSampleRate(rate);
+ _sampleConverter.setSampleRate(rate);
}
virtual void startSound(int sound);
virtual void stopSound(int sound);
diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp
index 2588026e59..33e3e40e39 100644
--- a/engines/scumm/player_towns.cpp
+++ b/engines/scumm/player_towns.cpp
@@ -87,7 +87,7 @@ void Player_Towns::restoreAfterLoad() {
if (!_v2)
restoredSounds.push_back(_pcmCurrentSound[i].index);
-
+
uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index);
if (!ptr)
continue;
diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp
index d4b21774ed..c1242e0645 100644
--- a/engines/scumm/player_v2cms.cpp
+++ b/engines/scumm/player_v2cms.cpp
@@ -718,38 +718,38 @@ void Player_V2CMS::playMusicChips(const MusicChip *table) {
}
const Player_V2CMS::MidiNote Player_V2CMS::_midiNotes[132] = {
- { 3, 0 }, { 31, 0 }, { 58, 0 }, { 83, 0 },
- { 107, 0 }, { 130, 0 }, { 151, 0 }, { 172, 0 },
- { 191, 0 }, { 209, 0 }, { 226, 0 }, { 242, 0 },
- { 3, 1 }, { 31, 1 }, { 58, 1 }, { 83, 1 },
- { 107, 1 }, { 130, 1 }, { 151, 1 }, { 172, 1 },
- { 191, 1 }, { 209, 1 }, { 226, 1 }, { 242, 1 },
- { 3, 2 }, { 31, 2 }, { 58, 2 }, { 83, 2 },
- { 107, 2 }, { 130, 2 }, { 151, 2 }, { 172, 2 },
- { 191, 2 }, { 209, 2 }, { 226, 2 }, { 242, 2 },
- { 3, 3 }, { 31, 3 }, { 58, 3 }, { 83, 3 },
- { 107, 3 }, { 130, 3 }, { 151, 3 }, { 172, 3 },
- { 191, 3 }, { 209, 3 }, { 226, 3 }, { 242, 3 },
- { 3, 4 }, { 31, 4 }, { 58, 4 }, { 83, 4 },
- { 107, 4 }, { 130, 4 }, { 151, 4 }, { 172, 4 },
- { 191, 4 }, { 209, 4 }, { 226, 4 }, { 242, 4 },
- { 3, 5 }, { 31, 5 }, { 58, 5 }, { 83, 5 },
- { 107, 5 }, { 130, 5 }, { 151, 5 }, { 172, 5 },
- { 191, 5 }, { 209, 5 }, { 226, 5 }, { 242, 5 },
- { 3, 6 }, { 31, 6 }, { 58, 6 }, { 83, 6 },
- { 107, 6 }, { 130, 6 }, { 151, 6 }, { 172, 6 },
- { 191, 6 }, { 209, 6 }, { 226, 6 }, { 242, 6 },
- { 3, 7 }, { 31, 7 }, { 58, 7 }, { 83, 7 },
- { 107, 7 }, { 130, 7 }, { 151, 7 }, { 172, 7 },
- { 191, 7 }, { 209, 7 }, { 226, 7 }, { 242, 7 },
- { 3, 8 }, { 31, 8 }, { 58, 8 }, { 83, 8 },
- { 107, 8 }, { 130, 8 }, { 151, 8 }, { 172, 8 },
- { 191, 8 }, { 209, 8 }, { 226, 8 }, { 242, 8 },
- { 3, 9 }, { 31, 9 }, { 58, 9 }, { 83, 9 },
- { 107, 9 }, { 130, 9 }, { 151, 9 }, { 172, 9 },
- { 191, 9 }, { 209, 9 }, { 226, 9 }, { 242, 9 },
- { 3, 10 }, { 31, 10 }, { 58, 10 }, { 83, 10 },
- { 107, 10 }, { 130, 10 }, { 151, 10 }, { 172, 10 },
+ { 3, 0 }, { 31, 0 }, { 58, 0 }, { 83, 0 },
+ { 107, 0 }, { 130, 0 }, { 151, 0 }, { 172, 0 },
+ { 191, 0 }, { 209, 0 }, { 226, 0 }, { 242, 0 },
+ { 3, 1 }, { 31, 1 }, { 58, 1 }, { 83, 1 },
+ { 107, 1 }, { 130, 1 }, { 151, 1 }, { 172, 1 },
+ { 191, 1 }, { 209, 1 }, { 226, 1 }, { 242, 1 },
+ { 3, 2 }, { 31, 2 }, { 58, 2 }, { 83, 2 },
+ { 107, 2 }, { 130, 2 }, { 151, 2 }, { 172, 2 },
+ { 191, 2 }, { 209, 2 }, { 226, 2 }, { 242, 2 },
+ { 3, 3 }, { 31, 3 }, { 58, 3 }, { 83, 3 },
+ { 107, 3 }, { 130, 3 }, { 151, 3 }, { 172, 3 },
+ { 191, 3 }, { 209, 3 }, { 226, 3 }, { 242, 3 },
+ { 3, 4 }, { 31, 4 }, { 58, 4 }, { 83, 4 },
+ { 107, 4 }, { 130, 4 }, { 151, 4 }, { 172, 4 },
+ { 191, 4 }, { 209, 4 }, { 226, 4 }, { 242, 4 },
+ { 3, 5 }, { 31, 5 }, { 58, 5 }, { 83, 5 },
+ { 107, 5 }, { 130, 5 }, { 151, 5 }, { 172, 5 },
+ { 191, 5 }, { 209, 5 }, { 226, 5 }, { 242, 5 },
+ { 3, 6 }, { 31, 6 }, { 58, 6 }, { 83, 6 },
+ { 107, 6 }, { 130, 6 }, { 151, 6 }, { 172, 6 },
+ { 191, 6 }, { 209, 6 }, { 226, 6 }, { 242, 6 },
+ { 3, 7 }, { 31, 7 }, { 58, 7 }, { 83, 7 },
+ { 107, 7 }, { 130, 7 }, { 151, 7 }, { 172, 7 },
+ { 191, 7 }, { 209, 7 }, { 226, 7 }, { 242, 7 },
+ { 3, 8 }, { 31, 8 }, { 58, 8 }, { 83, 8 },
+ { 107, 8 }, { 130, 8 }, { 151, 8 }, { 172, 8 },
+ { 191, 8 }, { 209, 8 }, { 226, 8 }, { 242, 8 },
+ { 3, 9 }, { 31, 9 }, { 58, 9 }, { 83, 9 },
+ { 107, 9 }, { 130, 9 }, { 151, 9 }, { 172, 9 },
+ { 191, 9 }, { 209, 9 }, { 226, 9 }, { 242, 9 },
+ { 3, 10 }, { 31, 10 }, { 58, 10 }, { 83, 10 },
+ { 107, 10 }, { 130, 10 }, { 151, 10 }, { 172, 10 },
{ 191, 10 }, { 209, 10 }, { 226, 10 }, { 242, 10 }
};
diff --git a/engines/scumm/proc3ARM.s b/engines/scumm/proc3ARM.s
index ca44386b5c..75dd4b4a7f 100644
--- a/engines/scumm/proc3ARM.s
+++ b/engines/scumm/proc3ARM.s
@@ -25,37 +25,40 @@
.global _ClassicProc3RendererShadowARM
-.set _scaleIndexY , 112
-.set _numStrips , 108
-.set _palette , 104
-.set _shadow_table , 100
-.set _scaleIndexX , 96
-.set _scaleX , 92
-.set _height , 88
-.set store_r14 , 84
-.set store_r11 , 80
-.set store_r10 , 76
-.set store_r9 , 72
-.set store_r8 , 68
-.set store_r7 , 64
-.set store_r6 , 60
-.set store_r5 , 56
-.set store_r4 , 52
-.set src , 48
-.set height , 44
-.set len , 40
-.set v1_shr , 36
-.set v1_skip_width , 32
-.set v1_destptr , 28
-.set v1_scaleXstep , 24
-.set v1_mask_ptr , 20
-.set v1_y , 16
-.set v1_scaletable , 12
-.set pitch , 8
-.set scaleIdxXPtr , 4
-.set scaleIdxYPtr , 0
-.set space , 48
+.set space, 48
+
+.set _scaleIndexY, store_r14 + 28
+.set _numStrips, store_r14 + 24
+.set _palette, store_r14 + 20
+.set _shadow_table, store_r14 + 16
+.set _scaleIndexX, store_r14 + 12
+.set _scaleX, store_r14 + 8
+.set _height, store_r14 + 4
+
+.set store_r14, space + 36
+.set store_r11, space + 32
+.set store_r10, space + 28
+.set store_r9, space + 24
+.set store_r8, space + 20
+.set store_r7, space + 16
+.set store_r6, space + 12
+.set store_r5, space + 8
+.set store_r4, space + 4
+
+.set src, 48
+.set height, 44
+.set len, 40
+.set v1_shr, 36
+.set v1_skip_width, 32
+.set v1_destptr, 28
+.set v1_scaleXstep, 24
+.set v1_mask_ptr, 20
+.set v1_y, 16
+.set v1_scaletable, 12
+.set pitch, 8
+.set scaleIdxXPtr, 4
+.set scaleIdxYPtr, 0
@ r0 = _scaleY
@ r1 = v1
@@ -103,7 +106,7 @@ _ClassicProc3RendererShadowARM:
LDRB r1, [r1,#30] @ r1 = repcolor
STR r8, [r13,#v1_shr]
STR r9, [r13,#v1_destptr]
- STR r10,[r13,#v1_mask_ptr]
+ STR r10,[r13,#v1_mask_ptr]
STR r11,[r13,#v1_scaleXstep]
LDR r12,[r13,#_height]
@@ -123,10 +126,10 @@ _ClassicProc3RendererShadowARM:
@ r12= _height
@ r14= v1.replen
- MOV r8,#0x80
+ MOV r8,#0x80
AND r11,r3,#7 @ r11= v1.x & 7
MOV r8,r8,LSR r11 @ r8 = maskbit = revBitMask(v1.x & 7)
- ADD r10,r10,r3,ASR #3 @ r10= mask = v1.mask_ptr + (v1.x>>3)
+ ADD r10,r10,r3,ASR #3 @ r10= mask = v1.mask_ptr + (v1.x>>3)
@ r0 = _scaleY
@ r1 = color = v1.repcolor
@@ -152,7 +155,7 @@ _ClassicProc3RendererShadowARM:
SUB r14,r14,r5
STR r12,[r13,#height]
STR r14,[r13,#len]
- LDR r12,[r13,#pitch]
+ LDR r12,[r13,#pitch]
LDR r11,[r13,#_numStrips]
B startpos
@@ -174,13 +177,13 @@ outerloop:
LDR r11,[r13,#src]
LDR r5,[r13,#v1_shr]
- @ stall
+ @ stall
LDRB r14,[r11],#1 @ r14= len = *src++
@ stall
@ stall
MOV r1, r14,LSR r5 @ r1 = color = len>>v1.shr
- BICS r14,r14,r1,LSL r5 @ r14= len
- LDREQB r14,[r11],#1 @ if (!len) r14 = len = *src++
+ BICS r14,r14,r1,LSL r5 @ r14= len
+ LDREQB r14,[r11],#1 @ if (!len) r14 = len = *src++
STR r11,[r13,#src]
CMP r14,#0
middleloop:
@@ -232,7 +235,7 @@ innerloop:
CMPLE r0,r14 @ || _scaleY >= r14
BLE startpos
- ADDS r4,r4,#1 @ y >= 0 (equiv to y>-1,y+1>0)
+ ADDS r4,r4,#1 @ y >= 0 (equiv to y>-1,y+1>0)
CMPGT r1,#0 @ && color > 0
CMPGT r6,r4 @ && _out.h+1 > y+1
CMNGT r3,#1 @ && x >= 0 (equiv to x>-1,x+1>0)
@@ -248,12 +251,12 @@ innerloop:
@ stall
@ stall
CMP r14,#13 @ if (pcolor == 13)
- LDREQ r12,[r13,#_shadow_table]
+ LDREQ r12,[r13,#_shadow_table]
LDREQB r14,[r9] @ r14 = *dst
@ stallEQ
@ stallEQ
LDREQB r14,[r12,r14] @ r14 = pcolor=_shadow_tab[r14]
- LDREQ r12,[r13,#pitch]
+ LDREQ r12,[r13,#pitch]
@ stallEQ
STRB r14,[r9] @ *dst = pcolor
masked:
@@ -282,7 +285,7 @@ startpos:
BLE noXstep
SUB r11,r7,#1
- ADDS r3,r3,r12 @ v1.x += v1.scaleXstep
+ ADDS r3,r3,r12 @ v1.x += v1.scaleXstep
@ if v1.x < 0 ||
CMPGE r11,r3 @ _out.w-1 < v1.x
BLT end
@@ -297,7 +300,7 @@ noXstep:
LDR r12,[r13,#_height] @ r12= height = _height
LDR r4,[r13,#v1_y] @ r4 = y = v1.y
LDR r2,[r13,#scaleIdxYPtr] @ r2 = v1.scaletable[sclIdxY]
- ADD r10,r10,r3,ASR #3 @ mask=v1.mask_ptr+(v1.x>>3)
+ ADD r10,r10,r3,ASR #3 @ mask=v1.mask_ptr+(v1.x>>3)
notheight:
CMP r14,#0 @ while (len > 0)
BGT middleloop
@@ -305,5 +308,5 @@ notheight:
end:
LDR r0,[r13,#v1_scaletable]
SUB r0,r2,r0
- ADD r13,r13,#space
+ ADD r13,r13,#space
LDMFD r13!,{r3-r11,PC}
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index beac077fd1..72896e097a 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -1298,7 +1298,7 @@ void ScummEngine::saveOrLoad(Serializer *s) {
s->saveLoadArrayOf(_16BitPalette, 512, sizeof(_16BitPalette[0]), sleUint16);
}
-
+
// FM-Towns specific (extra palette data, color cycle data, etc.)
// In earlier save game versions (below 87) the FM-Towns specific data would get saved (and loaded) even in non FM-Towns games.
// This would cause an unnecessary save file incompatibility between DS (which uses the DISABLE_TOWNS_DUAL_LAYER_MODE setting)
diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h
index d5f7ea526e..a640bc1e17 100644
--- a/engines/scumm/saveload.h
+++ b/engines/scumm/saveload.h
@@ -47,7 +47,7 @@ namespace Scumm {
* only saves/loads those which are valid for the version of the savegame
* which is being loaded/saved currently.
*/
-#define CURRENT_VER 92
+#define CURRENT_VER 93
/**
* An auxillary macro, used to specify savegame versions. We use this instead
diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp
index 44b77f1d18..361287d29f 100644
--- a/engines/scumm/script_v0.cpp
+++ b/engines/scumm/script_v0.cpp
@@ -602,7 +602,7 @@ void ScummEngine_v0::o_loadRoomWithEgo() {
x = r.x;
y = r.y;
a->putActor(x, y, _currentRoom);
-
+
camera._dest.x = camera._cur.x = a->getPos().x;
setCameraAt(a->getPos().x, a->getPos().y);
setCameraFollows(a);
@@ -635,18 +635,18 @@ void ScummEngine_v0::setMode(byte mode) {
case kModeCutscene:
_redrawSentenceLine = false;
// Note: do not change freeze state here
- state = USERSTATE_SET_IFACE |
+ state = USERSTATE_SET_IFACE |
USERSTATE_SET_CURSOR;
break;
case kModeKeypad:
_redrawSentenceLine = false;
- state = USERSTATE_SET_IFACE |
+ state = USERSTATE_SET_IFACE |
USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON |
USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON;
break;
case kModeNormal:
case kModeNoNewKid:
- state = USERSTATE_SET_IFACE | USERSTATE_IFACE_ALL |
+ state = USERSTATE_SET_IFACE | USERSTATE_IFACE_ALL |
USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON |
USERSTATE_SET_FREEZE;
break;
@@ -688,7 +688,7 @@ void ScummEngine_v0::o_animateActor() {
Actor_v0 *a = (Actor_v0*) derefActor(act, "o_animateActor");
a->_animFrameRepeat = repeat;
-
+
switch (anim) {
case 0xFE:
@@ -700,7 +700,7 @@ void ScummEngine_v0::o_animateActor() {
// 0x69A3
a->_speaking = 0x00;
return;
-
+
case 0xFF:
a->stopActorMoving();
return;
diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp
index ce162b4a6a..96d422d5bb 100644
--- a/engines/scumm/script_v2.cpp
+++ b/engines/scumm/script_v2.cpp
@@ -993,7 +993,7 @@ void ScummEngine_v2::o2_drawSentence() {
const byte *temp;
int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0);
- if (!((_userState & USERSTATE_IFACE_SENTENCE) ||
+ if (!((_userState & USERSTATE_IFACE_SENTENCE) ||
(_game.platform == Common::kPlatformNES && (_userState & USERSTATE_IFACE_ALL))))
return;
@@ -1486,8 +1486,8 @@ void ScummEngine_v2::o2_cutscene() {
VAR(VAR_CURSORSTATE) = 200;
// Hide inventory, freeze scripts, hide cursor
- setUserState(USERSTATE_SET_IFACE |
- USERSTATE_SET_CURSOR |
+ setUserState(USERSTATE_SET_IFACE |
+ USERSTATE_SET_CURSOR |
USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON);
_sentenceNum = 0;
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index a5591b701f..0bf51a2816 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -1097,7 +1097,7 @@ void ScummEngine_v5::o5_getDist() {
int r;
getResultPos();
-
+
o1 = getVarOrDirectWord(PARAM_1);
o2 = getVarOrDirectWord(PARAM_2);
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index d0f46f3e56..2c79fb8de0 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -73,6 +73,7 @@
#include "scumm/util.h"
#include "scumm/verbs.h"
#include "scumm/imuse/pcspk.h"
+#include "scumm/imuse/mac_m68k.h"
#include "backends/audiocd/audiocd.h"
@@ -1835,17 +1836,31 @@ void ScummEngine::setupMusic(int midi) {
} else if (_game.version >= 3 && _game.heversion <= 62) {
MidiDriver *nativeMidiDriver = 0;
MidiDriver *adlibMidiDriver = 0;
-
- if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK)
+ bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB);
+ bool useOnlyNative = false;
+
+ if (isMacM68kIMuse()) {
+ // We setup this driver as native MIDI driver to avoid playback
+ // of the Mac music via a selected MIDI device.
+ nativeMidiDriver = new MacM68kDriver(_mixer);
+ // The Mac driver is never MT-32.
+ _native_mt32 = false;
+ // Ignore non-native drivers. This also ignores the multi MIDI setting.
+ useOnlyNative = true;
+ } else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) {
nativeMidiDriver = MidiDriver::createMidi(dev);
+ }
+
if (nativeMidiDriver != NULL && _native_mt32)
nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
- bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB);
- if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) {
- adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB));
- adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
- } else if (_sound->_musicType == MDT_PCSPK) {
- adlibMidiDriver = new PcSpkDriver(_mixer);
+
+ if (!useOnlyNative) {
+ if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) {
+ adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB));
+ adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
+ } else if (_sound->_musicType == MDT_PCSPK) {
+ adlibMidiDriver = new PcSpkDriver(_mixer);
+ }
}
_imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver);
@@ -1971,11 +1986,11 @@ Common::Error ScummEngine::go() {
if (delta < 1) // Ensure we don't get into an endless loop
delta = 1; // by not decreasing sleepers.
- // WORKAROUND: walking speed in the original v0/v1 interpreter
+ // WORKAROUND: walking speed in the original v0/v1 interpreter
// is sometimes slower (e.g. during scrolling) than in ScummVM.
// This is important for the door-closing action in the dungeon,
- // otherwise (delta < 6) a single kid is able to escape.
- if ((_game.version == 0 && isScriptRunning(132)) ||
+ // otherwise (delta < 6) a single kid is able to escape.
+ if ((_game.version == 0 && isScriptRunning(132)) ||
(_game.version == 1 && isScriptRunning(137)))
delta = 6;
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index c8cf096a19..a77c1c0141 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -150,7 +150,13 @@ enum GameFeatures {
GF_HE_985 = 1 << 14,
/** HE games with 16 bit color */
- GF_16BIT_COLOR = 1 << 15
+ GF_16BIT_COLOR = 1 << 15,
+
+ /**
+ * SCUMM v5-v7 Mac games stored in a container file
+ * Used to differentiate between m68k and PPC versions of Indy4
+ */
+ GF_MAC_CONTAINER = 1 << 16
};
/* SCUMM Debug Channels */
@@ -713,6 +719,9 @@ public:
bool openFile(BaseScummFile &file, const Common::String &filename, bool resourceFile = false);
+ /** Is this game a Mac m68k v5 game with iMuse? */
+ bool isMacM68kIMuse() const;
+
protected:
int _resourceHeaderSize;
byte _resourceMapper[128];
@@ -1363,7 +1372,7 @@ public:
public:
bool towns_isRectInStringBox(int x1, int y1, int x2, int y2);
byte _townsPaletteFlags;
- byte _townsCharsetColorMap[16];
+ byte _townsCharsetColorMap[16];
protected:
void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h);
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 1dc026ad52..a1cecfa0b3 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -248,7 +248,10 @@ void Sound::playSound(int soundID) {
_mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID);
}
// Support for sampled sound effects in Monkey Island 1 and 2
- else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) {
+ else if (_vm->_game.platform != Common::kPlatformFMTowns
+ // The Macintosh m68k versions of MI2/Indy4 just ignore SBL effects.
+ && !_vm->isMacM68kIMuse()
+ && READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) {
debugC(DEBUG_SOUND, "Using SBL sound effect");
// SBL resources essentially contain VOC sound data.
@@ -954,7 +957,7 @@ void Sound::setupSfxFile() {
if (file.open(tmp))
_sfxFilename = tmp;
-
+
if (_vm->_game.heversion <= 74)
_sfxFileEncByte = 0x69;
@@ -1179,7 +1182,7 @@ int ScummEngine::readSoundResource(ResId idx) {
// its sound resources, and Amiga games, which feature only ROL
// resources, since we are a doing Midi -> AdLib conversion for
// these.
- if ((_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS) && pri != 16
+ if ((_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS) && pri != 16
&& pri != 15 && pri != 10 && pri != 2 && _game.platform != Common::kPlatformAmiga)
pri = -1;
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 567ca31485..0d0f6cdb95 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -84,7 +84,7 @@ int ScummEngine_v0::verbPrepIdType(int verbid) {
switch (verbid) {
case kVerbUse: // depends on object1
return kVerbPrepObject;
- case kVerbGive:
+ case kVerbGive:
return kVerbPrepTo;
case kVerbUnlock: case kVerbFix:
return kVerbPrepWith;
@@ -693,7 +693,7 @@ void ScummEngine_v0::verbExec() {
if (_activeVerb == kVerbWhatIs)
return;
-
+
if (!(_activeVerb == kVerbWalkTo && _activeObject == 0)) {
doSentence(_activeVerb, _activeObject, _activeObject2);
if (_activeVerb != kVerbWalkTo) {
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index dfa3ded50b..8f6c2bb6a2 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -119,12 +119,12 @@ GameList SkyMetaEngine::getSupportedGames() const {
const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &target) const {
Common::String guiOptions;
ExtraGuiOptions options;
-
+
if (target.empty()) {
options.push_back(skyExtraGuiOption);
return options;
}
-
+
if (ConfMan.hasKey("guioptions", target)) {
guiOptions = ConfMan.get("guioptions", target);
guiOptions = parseGameGUIOptions(guiOptions);
diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp
index f7add4eed2..ff3c897dba 100644
--- a/engines/sword1/animation.cpp
+++ b/engines/sword1/animation.cpp
@@ -69,7 +69,7 @@ static const char *const sequenceList[20] = {
};
// This is the list of the names of the PlayStation videos
-// TODO: fight.str, flashy.str,
+// TODO: fight.str, flashy.str,
static const char *const sequenceListPSX[20] = {
"e_ferr1",
"ladder1",
@@ -152,14 +152,14 @@ bool MoviePlayer::load(uint32 id) {
warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.c_str(), lineNo, startFrame, lastEnd);
continue;
}
-
+
int color = 0;
if (*ptr == '@') {
++ptr;
color = strtoul(ptr, const_cast<char **>(&ptr), 10);
while (*ptr && Common::isSpace(*ptr))
ptr++;
- }
+ }
_movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color));
lastEnd = endFrame;
diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp
index 6e395116f9..7cd85dff54 100644
--- a/engines/sword1/control.cpp
+++ b/engines/sword1/control.cpp
@@ -541,7 +541,9 @@ void Control::setupMainPanel() {
if (SwordEngine::_systemVars.controlPanelMode == CP_DEATHSCREEN)
panelId = SR_DEATHPANEL;
else {
- if (SwordEngine::_systemVars.language <= BS1_SPANISH)
+ if (SwordEngine::_systemVars.realLanguage == Common::EN_USA)
+ panelId = SR_PANEL_AMERICAN;
+ else if (SwordEngine::_systemVars.language <= BS1_SPANISH)
panelId = SR_PANEL_ENGLISH + SwordEngine::_systemVars.language;
else
panelId = SR_PANEL_ENGLISH;
diff --git a/engines/sword1/objectman.cpp b/engines/sword1/objectman.cpp
index 5d1864d58d..3e70a95699 100644
--- a/engines/sword1/objectman.cpp
+++ b/engines/sword1/objectman.cpp
@@ -107,7 +107,7 @@ char *ObjectMan::lockText(uint32 textId) {
warning("Missing translation for textId %u (\"%s\")", textId, text);
unlockText(textId, BS1_ENGLISH);
}
-
+
return _missingSubTitleStr;
}
return text;
@@ -164,7 +164,7 @@ char *ObjectMan::lockText(uint32 textId, uint8 lang) {
// We use the hardcoded text in this case.
if (textId == 2950145)
return const_cast<char *>(_translationId2950145[lang]);
-
+
warning("ObjectMan::lockText(%d): text number has no text lines", textId);
return NULL;
}
diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp
index 75e8f72d9d..fa593b8df4 100644
--- a/engines/sword1/sword1.cpp
+++ b/engines/sword1/sword1.cpp
@@ -116,8 +116,9 @@ Common::Error SwordEngine::init() {
_systemVars.controlPanelMode = CP_NEWGAME;
_systemVars.forceRestart = false;
_systemVars.wantFade = true;
+ _systemVars.realLanguage = Common::parseLanguage(ConfMan.get("language"));
- switch (Common::parseLanguage(ConfMan.get("language"))) {
+ switch (_systemVars.realLanguage) {
case Common::DE_DEU:
_systemVars.language = BS1_GERMAN;
break;
@@ -138,6 +139,7 @@ Common::Error SwordEngine::init() {
break;
default:
_systemVars.language = BS1_ENGLISH;
+ break;
}
_systemVars.showText = ConfMan.getBool("subtitles");
diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h
index ccdc2d3a59..ec6555b4b3 100644
--- a/engines/sword1/sword1.h
+++ b/engines/sword1/sword1.h
@@ -75,6 +75,7 @@ struct SystemVars {
uint8 language;
bool isDemo;
Common::Platform platform;
+ Common::Language realLanguage;
};
class SwordEngine : public Engine {
diff --git a/engines/sword2/sprite.cpp b/engines/sword2/sprite.cpp
index cb0923cc2f..91a5e2e86b 100644
--- a/engines/sword2/sprite.cpp
+++ b/engines/sword2/sprite.cpp
@@ -772,7 +772,7 @@ int32 Screen::drawSprite(SpriteInfo *s) {
src = sprite + rs.top * srcPitch + rs.left;
dst = _buffer + _screenWide * rd.top + rd.left;
- if (s->type & RDSPR_BLEND) {
+ if (s->type & RDSPR_BLEND) {
// The original code had two different blending cases. One for
// s->blend & 0x01 and one for s->blend & 0x02. However, the
// only values that actually appear in the cluster files are
@@ -783,7 +783,7 @@ int32 Screen::drawSprite(SpriteInfo *s) {
// The only correct way to simulate this would be using 16-bit mode.
// As this is not yet available for this engine, fake transparency is used
// as placeholder.
- if (!(_renderCaps & RDBLTFX_SPRITEBLEND) || Sword2Engine::isPsx()) {
+ if (!(_renderCaps & RDBLTFX_SPRITEBLEND) || Sword2Engine::isPsx()) {
for (i = 0; i < rs.height(); i++) {
for (j = 0; j < rs.width(); j++) {
if (src[j] && ((i & 1) == (j & 1)))
diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp
index 69fae3dc4e..61d53c89a7 100644
--- a/engines/sword25/sfx/soundengine.cpp
+++ b/engines/sword25/sfx/soundengine.cpp
@@ -370,7 +370,7 @@ bool SoundEngine::unpersist(InputPersistenceBlock &reader) {
int loopStart;
int loopEnd;
uint layer;
-
+
reader.readString(fileName);
reader.read(sndType);
reader.read(volume);
diff --git a/engines/tinsel/actors.cpp b/engines/tinsel/actors.cpp
index a784ff5788..0ba8b7cdba 100644
--- a/engines/tinsel/actors.cpp
+++ b/engines/tinsel/actors.cpp
@@ -319,8 +319,8 @@ static void ActorRestoredProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx);
_ctx->pic = RestoreInterpretContext(r->pic);
-
- // The newly added check here specially sets the process to RES_NOT when loading a savegame.
+
+ // The newly added check here specially sets the process to RES_NOT when loading a savegame.
// This is needed particularly for the Psychiatrist scene in Discworld 1 - otherwise Rincewind
// can't go upstairs without leaving the building and returning. If this patch causes problems
// in other scenes, an added check for the hCode == 1174490602 could be added.
diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp
index 6ea18c8268..04bc2856ca 100644
--- a/engines/tinsel/pcode.cpp
+++ b/engines/tinsel/pcode.cpp
@@ -152,7 +152,7 @@ static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491),
OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle
OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)};
static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)};
-static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58,
+static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58,
OP_IMM, FRAGMENT_DWORD((42 << 23)), OP_ONE, OP_ZERO, OP_LIBCALL | OPSIZE8, 44,
OP_LIBCALL | OPSIZE8, 97, OP_JUMP | OPSIZE16, FRAGMENT_WORD(2220)
};
@@ -222,7 +222,7 @@ const WorkaroundEntry workaroundList[] = {
// times would cause the game to crash
{TINSEL_V2, true, false, Common::kPlatformUnknown, 1109294728, 0, sizeof(fragment13), fragment13},
- // DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than
+ // DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than
// quitting the game when no user input happens for a while
{TINSEL_V1, true, true, Common::kPlatformPSX, 0, 2186, sizeof(fragment14), fragment14},
diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp
index 518e27f02b..2ef92d853f 100644
--- a/engines/tinsel/saveload.cpp
+++ b/engines/tinsel/saveload.cpp
@@ -502,7 +502,7 @@ static bool DoRestore() {
delete f; // Invalid header, or savegame too new -> skip it
return false;
}
-
+
// Load in the data. For older savegame versions, we potentially need to load the data twice, once
// for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames
int numInterpreters = hdr.numInterpreters;
diff --git a/engines/tinsel/scene.cpp b/engines/tinsel/scene.cpp
index 79bb30f7a3..c5444517f1 100644
--- a/engines/tinsel/scene.cpp
+++ b/engines/tinsel/scene.cpp
@@ -158,7 +158,8 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) {
// The following myEscape value setting is used for enabling title screen skipping in DW1
if (TinselV1 && (g_sceneCtr == 1)) g_initialMyEscape = GetEscEvents();
- _ctx->myEscape = (TinselV1 && (g_sceneCtr < 4)) ? g_initialMyEscape : 0;
+ // DW1 PSX has its own scene skipping script code for scenes 2 and 3 (bug #3541542).
+ _ctx->myEscape = (TinselV1 && (g_sceneCtr < (TinselV1PSX ? 2 : 4))) ? g_initialMyEscape : 0;
// get the stuff copied to process when it was created
_ctx->pInit = (const TP_INIT *)param;
diff --git a/engines/toltecs/animation.cpp b/engines/toltecs/animation.cpp
index eef9cef9ed..084332cf83 100644
--- a/engines/toltecs/animation.cpp
+++ b/engines/toltecs/animation.cpp
@@ -53,7 +53,7 @@ void AnimationPlayer::start(uint resIndex) {
_vm->_arc->closeResource();
debug(1, "AnimationPlayer::start() width = %d; height = %d; frameCount = %d", _width, _height, _frameCount);
-
+
_vm->_sceneWidth = _width;
_vm->_sceneHeight = _height;
@@ -63,7 +63,7 @@ void AnimationPlayer::start(uint resIndex) {
_frameNumber = 0;
// TODO mov screenFlag01, 0FFFFh
// TODO mov animDrawFrameFlag, 0FFFFh
-
+
_firstNextFrameOffset = _nextFrameOffset;
_firstCurFrameSize = _curFrameSize;
_firstNextFrameSize = _nextFrameSize;
@@ -81,25 +81,25 @@ void AnimationPlayer::nextFrame() {
} else {
_frameNumber++;
}
-
+
debug(1, "AnimationPlayer::nextFrame() frameNumber = %d", _frameNumber);
if (_keepFrameCounter > 0) {
_keepFrameCounter--;
return;
}
-
+
_vm->_arc->openResource(_resIndex);
_vm->_arc->seek(_nextFrameOffset, SEEK_CUR);
_curFrameSize = _nextFrameSize;
-
+
if (_curFrameSize == 0)
_curFrameSize = 1;
-
+
_vm->_arc->read(_animBuffer, _curFrameSize);
_nextFrameSize = _vm->_arc->readUint32LE();
_nextFrameOffset += _curFrameSize + 4;
-
+
if (_curFrameSize > 1) {
unpackFrame();
// TODO mov animDrawFrameFlag, 0FFFFh
diff --git a/engines/toltecs/animation.h b/engines/toltecs/animation.h
index 22576d7535..54ec5d8afa 100644
--- a/engines/toltecs/animation.h
+++ b/engines/toltecs/animation.h
@@ -54,7 +54,7 @@ public:
uint16 _width, _height;
uint16 _frameNumber, _frameCount;
uint32 _keepFrameCounter;
-
+
uint32 _curFrameSize;
uint32 _nextFrameSize, _nextFrameOffset;
diff --git a/engines/toltecs/menu.cpp b/engines/toltecs/menu.cpp
index 415f19ca31..6e23ff988f 100644
--- a/engines/toltecs/menu.cpp
+++ b/engines/toltecs/menu.cpp
@@ -21,8 +21,11 @@
*
*/
+#include "audio/mixer.h"
#include "common/savefile.h"
+#include "common/config-manager.h"
+
#include "toltecs/toltecs.h"
#include "toltecs/menu.h"
#include "toltecs/palette.h"
@@ -37,7 +40,7 @@ MenuSystem::MenuSystem(ToltecsEngine *vm) : _vm(vm) {
MenuSystem::~MenuSystem() {
}
-int MenuSystem::run() {
+int MenuSystem::run(MenuID menuId) {
//debug("MenuSystem::run()");
@@ -50,18 +53,13 @@ int MenuSystem::run() {
memcpy(backgroundOrig.getBasePtr(0,0), _vm->_screen->_frontScreen, 640 * 400);
_currMenuID = kMenuIdNone;
- _newMenuID = kMenuIdMain;
+ _newMenuID = menuId;
_currItemID = kItemIdNone;
_editingDescription = false;
- _cfgText = true;
- _cfgVoices = true;
- _cfgMasterVolume = 10;
- _cfgVoicesVolume = 10;
- _cfgMusicVolume = 10;
- _cfgSoundFXVolume = 10;
- _cfgBackgroundVolume = 10;
- _running = true;
+
+ _running = true;
_top = 30 - _vm->_guiHeight / 2;
+
_needRedraw = false;
// TODO: buildColorTransTable2
@@ -78,7 +76,7 @@ int MenuSystem::run() {
update();
_vm->_system->updateScreen();
}
-
+
// Restore original background
memcpy(_vm->_screen->_frontScreen, backgroundOrig.getBasePtr(0,0), 640 * 400);
_vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, 0, 640, 400);
@@ -89,7 +87,7 @@ int MenuSystem::run() {
_background->free();
delete _background;
- return 0;
+ return 0;
}
void MenuSystem::update() {
@@ -104,7 +102,7 @@ void MenuSystem::update() {
if (_needRedraw) {
//_vm->_system->copyRectToScreen(_vm->_screen->_frontScreen + 39 * 640 + 60, 640, 60, 39, 520, 247);
- _vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, 0, 640, 400);
+ _vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, _top, 640, 400 - _top);
//debug("redraw");
_needRedraw = false;
}
@@ -204,7 +202,7 @@ void MenuSystem::handleKeyDown(const Common::KeyState& kbd) {
ItemID MenuSystem::findItemAt(int x, int y) {
for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) {
- if ((*iter).rect.contains(x, y))
+ if ((*iter).rect.contains(x, y - _top))
return (*iter).id;
}
return kItemIdNone;
@@ -241,8 +239,8 @@ void MenuSystem::initMenu(MenuID menuID) {
drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrWhatCanIDoForYou));
addClickTextItem(kItemIdLoad, 0, 115, 320, 0, _vm->getSysString(kStrLoad), 229, 255);
addClickTextItem(kItemIdSave, 0, 135, 320, 0, _vm->getSysString(kStrSave), 229, 255);
- addClickTextItem(kItemIdToggleText, 0, 165, 320, 0, _vm->getSysString(kStrTextOn), 229, 255);
- addClickTextItem(kItemIdToggleVoices, 0, 185, 320, 0, _vm->getSysString(kStrVoicesOn), 229, 255);
+ addClickTextItem(kItemIdToggleText, 0, 165, 320, 0, _vm->getSysString(_vm->_cfgText ? kStrTextOn : kStrTextOff), 229, 255);
+ addClickTextItem(kItemIdToggleVoices, 0, 185, 320, 0, _vm->getSysString(_vm->_cfgVoices ? kStrVoicesOn : kStrVoicesOff), 229, 255);
addClickTextItem(kItemIdVolumesMenu, 0, 215, 320, 0, _vm->getSysString(kStrVolume), 229, 255);
addClickTextItem(kItemIdPlay, 0, 245, 320, 0, _vm->getSysString(kStrPlay), 229, 255);
addClickTextItem(kItemIdQuit, 0, 275, 320, 0, _vm->getSysString(kStrQuit), 229, 255);
@@ -326,13 +324,13 @@ void MenuSystem::clickItem(ItemID id) {
_newMenuID = kMenuIdLoad;
break;
case kItemIdToggleText:
- setCfgText(!_cfgText, true);
- if (!_cfgVoices && !_cfgText)
+ setCfgText(!_vm->_cfgText, true);
+ if (!_vm->_cfgVoices && !_vm->_cfgText)
setCfgVoices(true, false);
break;
case kItemIdToggleVoices:
- setCfgVoices(!_cfgVoices, true);
- if (!_cfgVoices && !_cfgText)
+ setCfgVoices(!_vm->_cfgVoices, true);
+ if (!_vm->_cfgVoices && !_vm->_cfgText)
setCfgText(true, false);
break;
case kItemIdVolumesMenu:
@@ -416,7 +414,7 @@ void MenuSystem::restoreRect(int x, int y, int w, int h) {
}
void MenuSystem::shadeRect(int x, int y, int w, int h, byte color1, byte color2) {
- byte *src = (byte *)_background->getBasePtr(x, y);
+ byte *src = (byte *)_vm->_screen->_frontScreen + x + y * 640;
for (int xc = 0; xc < w; xc++) {
src[xc] = color2;
src[xc + h * 640] = color1;
@@ -518,49 +516,51 @@ void MenuSystem::clickSavegameItem(ItemID id) {
}
void MenuSystem::setCfgText(bool value, bool active) {
- if (_cfgText != value) {
+ if (_vm->_cfgText != value) {
Item *item = getItem(kItemIdToggleText);
- _cfgText = value;
+ _vm->_cfgText = value;
restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2);
- setItemCaption(item, _vm->getSysString(_cfgText ? kStrTextOn : kStrTextOff));
+ setItemCaption(item, _vm->getSysString(_vm->_cfgText ? kStrTextOn : kStrTextOff));
drawItem(kItemIdToggleText, true);
+ ConfMan.setBool("subtitles", value);
}
}
void MenuSystem::setCfgVoices(bool value, bool active) {
- if (_cfgVoices != value) {
+ if (_vm->_cfgVoices != value) {
Item *item = getItem(kItemIdToggleVoices);
- _cfgVoices = value;
+ _vm->_cfgVoices = value;
restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2);
- setItemCaption(item, _vm->getSysString(_cfgVoices ? kStrVoicesOn : kStrVoicesOff));
+ setItemCaption(item, _vm->getSysString(_vm->_cfgVoices ? kStrVoicesOn : kStrVoicesOff));
drawItem(kItemIdToggleVoices, true);
+ ConfMan.setBool("speech_mute", !value);
}
}
void MenuSystem::drawVolumeBar(ItemID itemID) {
int w = 440, y, volume;
char text[21];
-
+
switch (itemID) {
- case kItemIdMaster:
+ case kItemIdMaster: // unused in ScummVM, always 20
y = 130 + 25 * 0;
- volume = _cfgMasterVolume;
+ volume = 20;
break;
case kItemIdVoices:
y = 130 + 25 * 1;
- volume = _cfgVoicesVolume;
+ volume = _vm->_cfgVoicesVolume;
break;
case kItemIdMusic:
y = 130 + 25 * 2;
- volume = _cfgMusicVolume;
+ volume = _vm->_cfgMusicVolume;
break;
case kItemIdSoundFX:
y = 130 + 25 * 3;
- volume = _cfgSoundFXVolume;
+ volume = _vm->_cfgSoundFXVolume;
break;
- case kItemIdBackground:
+ case kItemIdBackground: // unused in ScummVM, always 20
y = 130 + 25 * 4;
- volume = _cfgBackgroundVolume;
+ volume = 20;
break;
default:
return;
@@ -568,46 +568,48 @@ void MenuSystem::drawVolumeBar(ItemID itemID) {
Font font(_vm->_res->load(_vm->_screen->getFontResIndex(1))->data);
restoreRect(390, y - font.getHeight(), 100, 25);
-
+
for (int i = 0; i < volume; i++)
text[i] = '|';
text[volume] = 0;
-
+
drawString(0, y, w, 0, 246, text);
-
+
}
void MenuSystem::changeVolumeBar(ItemID itemID, int delta) {
-
- int *volume, newVolume;
+ byte newVolume;
switch (itemID) {
- case kItemIdMaster:
- volume = &_cfgMasterVolume;
- break;
case kItemIdVoices:
- volume = &_cfgVoicesVolume;
+ _vm->_cfgVoicesVolume = CLIP(_vm->_cfgVoicesVolume + delta, 0, 20);
+ // Always round volume up instead of down.
+ newVolume = (_vm->_cfgVoicesVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20;
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, newVolume);
+ ConfMan.setInt("speech_volume", newVolume);
break;
case kItemIdMusic:
- volume = &_cfgMusicVolume;
+ _vm->_cfgMusicVolume = CLIP(_vm->_cfgMusicVolume + delta, 0, 20);
+ newVolume = (_vm->_cfgMusicVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20;
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, newVolume);
+ ConfMan.setInt("music_volume", newVolume);
break;
case kItemIdSoundFX:
- volume = &_cfgSoundFXVolume;
+ _vm->_cfgSoundFXVolume = CLIP(_vm->_cfgSoundFXVolume + delta, 0, 20);
+ newVolume = (_vm->_cfgSoundFXVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20;
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, newVolume);
+ ConfMan.setInt("sfx_volume", newVolume);
break;
+ case kItemIdMaster:
case kItemIdBackground:
- volume = &_cfgBackgroundVolume;
+ // unused in ScummVM
break;
default:
return;
}
- newVolume = CLIP(*volume + delta, 0, 20);
-
- if (newVolume != *volume) {
- *volume = newVolume;
- drawVolumeBar(itemID);
- }
-
+ _vm->syncSoundSettings();
+ drawVolumeBar(itemID);
}
} // End of namespace Toltecs
diff --git a/engines/toltecs/menu.h b/engines/toltecs/menu.h
index 3e2c2da8d9..a72205c2e5 100644
--- a/engines/toltecs/menu.h
+++ b/engines/toltecs/menu.h
@@ -29,14 +29,6 @@
namespace Toltecs {
-enum MenuID {
- kMenuIdNone,
- kMenuIdMain,
- kMenuIdSave,
- kMenuIdLoad,
- kMenuIdVolumes
-};
-
enum ItemID {
kItemIdNone,
// Main menu
@@ -85,10 +77,10 @@ public:
MenuSystem(ToltecsEngine *vm);
~MenuSystem();
- int run();
+ int run(MenuID menuId);
void update();
void handleEvents();
-
+
protected:
struct Item {
@@ -99,7 +91,7 @@ protected:
int x, y, w;
uint fontNum;
};
-
+
struct SavegameItem {
int _slotNum;
Common::String _description;
@@ -124,9 +116,6 @@ protected:
Common::Array<Item> _items;
Common::Array<SavegameItem> _savegames;
-
- bool _cfgText, _cfgVoices;
- int _cfgMasterVolume, _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume, _cfgBackgroundVolume;
void addClickTextItem(ItemID id, int x, int y, int w, uint fontNum, const char *caption, byte defaultColor, byte activeColor);
@@ -134,13 +123,13 @@ protected:
void handleMouseMove(int x, int y);
void handleMouseClick(int x, int y);
void handleKeyDown(const Common::KeyState& kbd);
-
+
ItemID findItemAt(int x, int y);
Item *getItem(ItemID id);
void setItemCaption(Item *item, const char *caption);
void initMenu(MenuID menuID);
-
+
void enterItem(ItemID id);
void leaveItem(ItemID id);
void clickItem(ItemID id);
diff --git a/engines/toltecs/microtiles.cpp b/engines/toltecs/microtiles.cpp
index 0b61ac38a5..60e65bdaf3 100644
--- a/engines/toltecs/microtiles.cpp
+++ b/engines/toltecs/microtiles.cpp
@@ -138,7 +138,7 @@ Common::Rect * MicroTileArray::getRectangles(int *num_rects, int min_x, int min_
x0 = CLIP (x0, min_x, max_x);
y0 = CLIP (y0, min_y, max_y);
y1 = CLIP (y1, min_y, max_y);
-
+
// FIXME: Why is the following code in an #if block?
#if 1
start = i;
diff --git a/engines/toltecs/movie.cpp b/engines/toltecs/movie.cpp
index 76d42ebf0a..35accb5d93 100644
--- a/engines/toltecs/movie.cpp
+++ b/engines/toltecs/movie.cpp
@@ -61,7 +61,7 @@ void MoviePlayer::playMovie(uint resIndex) {
int16 savedCameraY = _vm->_cameraY;
int16 savedGuiHeight = _vm->_guiHeight;
byte moviePalette[768];
-
+
_vm->_isSaveAllowed = false;
memset(moviePalette, 0, sizeof(moviePalette));
@@ -78,7 +78,7 @@ void MoviePlayer::playMovie(uint resIndex) {
_vm->_arc->readUint32LE();
_vm->_arc->readUint32LE();
_framesPerSoundChunk = _vm->_arc->readUint32LE();
- _vm->_arc->readUint32LE();
+ int rate = _vm->_arc->readUint32LE();
_vm->_sceneWidth = 640;
_vm->_sceneHeight = 400;
@@ -87,7 +87,7 @@ void MoviePlayer::playMovie(uint resIndex) {
_vm->_cameraY = 0;
_vm->_guiHeight = 0;
- _audioStream = Audio::makeQueuingAudioStream(22050, false);
+ _audioStream = Audio::makeQueuingAudioStream(rate, false);
_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle, _audioStream);
@@ -96,49 +96,52 @@ void MoviePlayer::playMovie(uint resIndex) {
fetchAudioChunks();
- uint32 lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle);
+ byte *chunkBuffer = NULL;
+ uint32 chunkBufferSize = 0;
+ uint32 frame = 0;
while (_chunkCount--) {
-
byte chunkType = _vm->_arc->readByte();
uint32 chunkSize = _vm->_arc->readUint32LE();
- byte *chunkBuffer = NULL;
- uint32 movieOffset;
debug(0, "chunkType = %d; chunkSize = %d", chunkType, chunkSize);
-
+
// Skip audio chunks - we've already queued them in
// fetchAudioChunks() above
if (chunkType == kChunkAudio) {
_vm->_arc->skip(chunkSize);
} else {
- chunkBuffer = new byte[chunkSize];
+ // Only reallocate the chunk buffer if the new chunk is bigger
+ if (chunkSize > chunkBufferSize) {
+ delete[] chunkBuffer;
+ chunkBuffer = new byte[chunkSize];
+ chunkBufferSize = chunkSize;
+ }
+
_vm->_arc->read(chunkBuffer, chunkSize);
}
- movieOffset = _vm->_arc->pos();
-
switch (chunkType) {
case kChunkFirstImage:
case kChunkSubsequentImages:
unpackRle(chunkBuffer, _vm->_screen->_backScreen);
- // TODO: Rework this
- _vm->_screen->updateShakeScreen();
_vm->_screen->_fullRefresh = true;
- _vm->updateInput();
- _vm->drawScreen();
_soundChunkFramesLeft--;
if (_soundChunkFramesLeft <= _framesPerSoundChunk) {
fetchAudioChunks();
}
- while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < lastTime + 111) {
- g_system->delayMillis(10);
+ while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < (1000 * frame) / 9) {
+ if (_vm->_screen->_shakeActive && _vm->_screen->updateShakeScreen()) {
+ _vm->_screen->_fullRefresh = true;
+ }
+ _vm->updateInput();
+ _vm->drawScreen();
+ // Note: drawScreen() calls delayMillis()
}
- lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle);
-
+ frame++;
break;
case kChunkPalette:
unpackPalette(chunkBuffer, moviePalette, 256, 3);
@@ -150,10 +153,10 @@ void MoviePlayer::playMovie(uint resIndex) {
// Already processed
break;
case kChunkShowSubtitle:
- // TODO: Check if the text is a subtitle (last character == 0xFE).
- // If so, don't show it if text display is disabled.
- memcpy(_vm->_script->getSlotData(subtitleSlot), chunkBuffer, chunkSize);
- _vm->_screen->updateTalkText(subtitleSlot, 0);
+ if (_vm->_cfgText) {
+ memcpy(_vm->_script->getSlotData(subtitleSlot), chunkBuffer, chunkSize);
+ _vm->_screen->updateTalkText(subtitleSlot, 0);
+ }
break;
case kChunkShakeScreen: // start/stop shakescreen effect
if (chunkBuffer[0] == 0xFF)
@@ -176,20 +179,17 @@ void MoviePlayer::playMovie(uint resIndex) {
error("MoviePlayer::playMovie(%04X) Unknown chunk type %d at %08X", resIndex, chunkType, _vm->_arc->pos() - 5 - chunkSize);
}
- delete[] chunkBuffer;
-
- _vm->_arc->seek(movieOffset, SEEK_SET);
-
if (!handleInput())
break;
-
}
+ delete[] chunkBuffer;
+
_audioStream->finish();
_vm->_mixer->stopHandle(_audioStreamHandle);
_vm->_arc->closeResource();
-
+
debug(0, "playMovie() done");
_vm->_sceneWidth = savedSceneWidth;
diff --git a/engines/toltecs/movie.h b/engines/toltecs/movie.h
index aecfac240f..8fa48975d7 100644
--- a/engines/toltecs/movie.h
+++ b/engines/toltecs/movie.h
@@ -36,7 +36,7 @@ public:
~MoviePlayer();
void playMovie(uint resIndex);
-
+
protected:
ToltecsEngine *_vm;
Audio::QueuingAudioStream *_audioStream;
@@ -47,11 +47,11 @@ protected:
void unpackPalette(byte *source, byte *dest, int elemCount, int elemSize);
void unpackRle(byte *source, byte *dest);
-
+
void fetchAudioChunks();
-
+
bool handleInput();
-
+
};
} // End of namespace Toltecs
diff --git a/engines/toltecs/music.cpp b/engines/toltecs/music.cpp
index c322961077..830e4a97da 100644
--- a/engines/toltecs/music.cpp
+++ b/engines/toltecs/music.cpp
@@ -20,15 +20,13 @@
*
*/
-// FIXME: This code is taken from SAGA and needs more work (e.g. setVolume).
+#include "audio/midiparser.h"
+#include "common/textconsole.h"
#include "toltecs/toltecs.h"
#include "toltecs/music.h"
#include "toltecs/resource.h"
-#include "audio/midiparser.h"
-#include "common/textconsole.h"
-
namespace Toltecs {
MusicPlayer::MusicPlayer(bool isGM) : _isGM(isGM), _buffer(NULL) {
@@ -62,7 +60,7 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) {
memcpy(_buffer, data, size);
MidiParser *parser;
-
+
if (!memcmp(data, "FORM", 4))
parser = MidiParser::createParser_XMIDI(NULL);
else
@@ -77,7 +75,7 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) {
_parser = parser;
- setVolume(127);
+ syncVolume();
_isLooping = loop;
_isPlaying = true;
@@ -86,16 +84,6 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) {
}
}
-void MusicPlayer::pause() {
- setVolume(-1);
- _isPlaying = false;
-}
-
-void MusicPlayer::resume() {
- setVolume(127);
- _isPlaying = true;
-}
-
void MusicPlayer::stopAndClear() {
Common::StackLock lock(_mutex);
stop();
diff --git a/engines/toltecs/music.h b/engines/toltecs/music.h
index 79df1ea2f5..8d364dbb9f 100644
--- a/engines/toltecs/music.h
+++ b/engines/toltecs/music.h
@@ -37,8 +37,6 @@ public:
MusicPlayer(bool isGM = true);
void playMIDI(const byte *data, uint32 size, bool loop = false);
- void pause();
- void resume();
void stopAndClear();
// MidiDriver_BASE interface implementation
diff --git a/engines/toltecs/palette.cpp b/engines/toltecs/palette.cpp
index 706218e0ba..74683c6d7a 100644
--- a/engines/toltecs/palette.cpp
+++ b/engines/toltecs/palette.cpp
@@ -31,7 +31,7 @@ namespace Toltecs {
Palette::Palette(ToltecsEngine *vm) : _vm(vm) {
clearFragments();
-
+
memset(_colorTransTable, 0, sizeof(_colorTransTable));
}
@@ -81,7 +81,7 @@ void Palette::setDeltaPalette(byte *palette, byte mask, int8 deltaValue, int16 c
if (mask & 4) colors[index * 3 + 2] = CLIP<int>(rgb + deltaValue, 0, 63) << 2;
index++;
}
-
+
debug(0, "startIndex = %d; colorCount = %d", startIndex, colorCount);
_vm->_system->getPaletteManager()->setPalette((const byte *)colors, 0, 256);
@@ -101,9 +101,9 @@ void Palette::addFragment(uint resIndex, int16 id) {
Resource *fragmentResource = _vm->_res->load(resIndex);
byte count = fragmentResource->size / 3;
-
+
memcpy(&_mainPalette[_fragmentIndex * 3], fragmentResource->data, count * 3);
-
+
PaletteFragment fragment;
fragment.id = id;
fragment.index = _fragmentIndex;
@@ -126,7 +126,7 @@ uint16 Palette::findFragment(int16 id) {
break;
}
}
-
+
debug(0, "Palette::findFragment() result = %04X", result);
return result;
@@ -140,9 +140,9 @@ void Palette::clearFragments() {
void Palette::buildColorTransTable(byte limit, int8 deltaValue, byte mask) {
byte r = 0, g = 0, b = 0;
-
+
mask &= 7;
-
+
for (int i = 0; i < 256; i++) {
if (deltaValue < 0) {
@@ -161,7 +161,7 @@ void Palette::buildColorTransTable(byte limit, int8 deltaValue, byte mask) {
b -= deltaValue;
}
}
-
+
int bestIndex = 0;
uint16 bestMatch = 0xFFFF;
@@ -174,7 +174,7 @@ void Palette::buildColorTransTable(byte limit, int8 deltaValue, byte mask) {
bestIndex = j;
}
}
-
+
_colorTransTable[i] = bestIndex;
}
diff --git a/engines/toltecs/palette.h b/engines/toltecs/palette.h
index 7bcf06e027..570f51777e 100644
--- a/engines/toltecs/palette.h
+++ b/engines/toltecs/palette.h
@@ -66,7 +66,7 @@ protected:
int16 id;
byte index, count;
};
-
+
typedef Common::Array<PaletteFragment> PaletteFragmentArray;
ToltecsEngine *_vm;
diff --git a/engines/toltecs/render.cpp b/engines/toltecs/render.cpp
index 3f5356493e..4c41e6ce00 100644
--- a/engines/toltecs/render.cpp
+++ b/engines/toltecs/render.cpp
@@ -114,7 +114,7 @@ void RenderQueue::addMask(SegmapMaskRect &mask) {
void RenderQueue::update() {
bool doFullRefresh = _vm->_screen->_fullRefresh;
-
+
_updateUta->clear();
if (!doFullRefresh) {
@@ -166,7 +166,7 @@ void RenderQueue::update() {
for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
const RenderQueueItem *item = &(*iter);
-
+
if (item->flags == kRefresh || doFullRefresh) {
switch (item->type) {
@@ -200,7 +200,7 @@ void RenderQueue::update() {
SWAP(_currQueue, _prevQueue);
_currQueue->clear();
-
+
}
void RenderQueue::clear() {
@@ -249,16 +249,16 @@ bool RenderQueue::hasItemChanged(const RenderQueueItem &item1, const RenderQueue
if (item1.type != item2.type)
return true;
-
+
if (item1.rect.left != item2.rect.left ||
item1.rect.top != item2.rect.top ||
item1.rect.right != item2.rect.right ||
item1.rect.bottom != item2.rect.bottom)
return true;
-
+
if (item1.type == kText && item1.text.color != item2.text.color)
return true;
-
+
return false;
}
@@ -268,7 +268,7 @@ void RenderQueue::invalidateItemsByRect(const Common::Rect &rect, const RenderQu
if (item != subItem &&
subItem->flags == kUnchanged &&
rect.intersects(subItem->rect)) {
-
+
subItem->flags = kRefresh;
invalidateItemsByRect(subItem->rect, subItem);
}
diff --git a/engines/toltecs/render.h b/engines/toltecs/render.h
index bb9ec29959..59d7a3ddb9 100644
--- a/engines/toltecs/render.h
+++ b/engines/toltecs/render.h
@@ -75,7 +75,7 @@ public:
void addMask(SegmapMaskRect &mask);
void update();
void clear();
-
+
protected:
typedef Common::List<RenderQueueItem> RenderQueueArray;
@@ -87,7 +87,7 @@ protected:
RenderQueueItem *findItemInQueue(RenderQueueArray *queue, const RenderQueueItem &item);
bool hasItemChanged(const RenderQueueItem &item1, const RenderQueueItem &item2);
void invalidateItemsByRect(const Common::Rect &rect, const RenderQueueItem *item);
-
+
void addDirtyRect(const Common::Rect &rect);
void restoreDirtyBackground();
void updateDirtyRects();
diff --git a/engines/toltecs/resource.cpp b/engines/toltecs/resource.cpp
index b95e0444b1..0b9f7c8fcd 100644
--- a/engines/toltecs/resource.cpp
+++ b/engines/toltecs/resource.cpp
@@ -66,7 +66,7 @@ void ArchiveReader::dump(uint resIndex, const char *prefix) {
byte *data = new byte[resourceSize];
Common::String fn;
-
+
if (prefix)
fn = Common::String::format("%s_%04X.0", prefix, resIndex);
else
@@ -117,11 +117,11 @@ Resource *ResourceCache::load(uint resIndex) {
resItem->data = new byte[resItem->size];
_vm->_arc->read(resItem->data, resItem->size);
_vm->_arc->closeResource();
-
+
_cache[resIndex] = resItem;
-
+
return resItem;
-
+
}
}
diff --git a/engines/toltecs/saveload.cpp b/engines/toltecs/saveload.cpp
index c24d2149b0..6c195a34c2 100644
--- a/engines/toltecs/saveload.cpp
+++ b/engines/toltecs/saveload.cpp
@@ -36,12 +36,11 @@
namespace Toltecs {
/* TODO:
- - Save with F7; Load with F9
- Saving during an animation (AnimationPlayer) is not working correctly yet
- Maybe switch to SCUMM/Tinsel serialization approach?
*/
-#define TOLTECS_SAVEGAME_VERSION 3
+#define TOLTECS_SAVEGAME_VERSION 4
ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) {
@@ -93,7 +92,7 @@ void ToltecsEngine::savegame(const char *filename, const char *description) {
byte descriptionLen = strlen(description);
out->writeByte(descriptionLen);
out->write(description, descriptionLen);
-
+
Graphics::saveThumbnail(*out);
// Not used yet, reserved for future usage
@@ -141,8 +140,8 @@ void ToltecsEngine::savegame(const char *filename, const char *description) {
}
void ToltecsEngine::loadgame(const char *filename) {
- Common::InSaveFile *in;
- if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
+ if (!in) {
warning("Can't open file '%s', game not loaded", filename);
return;
}
@@ -150,13 +149,13 @@ void ToltecsEngine::loadgame(const char *filename) {
SaveHeader header;
kReadSaveHeaderError errorCode = readSaveHeader(in, false, header);
-
+
if (errorCode != kRSHENoError) {
warning("Error loading savegame '%s'", filename);
delete in;
return;
}
-
+
_sound->stopAll();
_music->stopSequence();
g_engine->setTotalPlayTime(header.playTime * 1000);
@@ -182,7 +181,7 @@ void ToltecsEngine::loadgame(const char *filename) {
_mouseX = in->readUint16LE();
_mouseY = in->readUint16LE();
_mouseDisabled = in->readUint16LE();
-
+
_system->warpMouse(_mouseX, _mouseY);
_system->showMouse(_mouseDisabled == 0);
@@ -191,7 +190,7 @@ void ToltecsEngine::loadgame(const char *filename) {
_anim->loadState(in);
_screen->loadState(in);
if (header.version >= 2)
- _sound->loadState(in);
+ _sound->loadState(in, header.version);
if (header.version >= 3)
_music->loadState(in);
diff --git a/engines/toltecs/screen.cpp b/engines/toltecs/screen.cpp
index 634917a7b1..c8d6740b02 100644
--- a/engines/toltecs/screen.cpp
+++ b/engines/toltecs/screen.cpp
@@ -43,9 +43,11 @@ Screen::Screen(ToltecsEngine *vm) : _vm(vm) {
// Screen shaking
_shakeActive = false;
+ _shakeTime = 0;
_shakeCounterInit = 0;
_shakeCounter = 0;
_shakePos = 0;
+ _shakeTime = 0;
// Verb line
_verbLineNum = 0;
@@ -73,7 +75,7 @@ Screen::~Screen() {
delete[] _frontScreen;
delete[] _backScreen;
-
+
delete _renderQueue;
}
@@ -129,7 +131,7 @@ void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) {
byte *dest = _frontScreen + x + (y + _vm->_cameraHeight) * 640;
//debug(0, "Screen::drawGuiImage() x = %d; y = %d; w = %d; h = %d; resIndex = %d", x, y, width, height, resIndex);
-
+
while (workHeight > 0) {
int count = 1;
byte pixel = *imageData++;
@@ -156,6 +158,7 @@ void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) {
void Screen::startShakeScreen(int16 shakeCounter) {
_shakeActive = true;
+ _shakeTime = 0;
_shakeCounterInit = shakeCounter;
_shakeCounter = shakeCounter;
_shakePos = 0;
@@ -166,15 +169,19 @@ void Screen::stopShakeScreen() {
_vm->_system->setShakePos(0);
}
-void Screen::updateShakeScreen() {
- if (_shakeActive) {
+bool Screen::updateShakeScreen() {
+ // Assume shaking happens no more often than 50 times per second
+ if (_shakeActive && _vm->_system->getMillis() - _shakeTime >= 20) {
+ _shakeTime = _vm->_system->getMillis();
_shakeCounter--;
if (_shakeCounter == 0) {
_shakeCounter = _shakeCounterInit;
_shakePos ^= 8;
_vm->_system->setShakePos(_shakePos);
+ return true;
}
}
+ return false;
}
void Screen::addStaticSprite(byte *spriteItem) {
@@ -247,7 +254,7 @@ void Screen::addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, i
} else {
loopNum |= 0x8000;
}
-
+
WRITE_LE_UINT16(spriteItem + 0, loopNum);
WRITE_LE_UINT16(spriteItem + 4, frameNum);
@@ -308,9 +315,9 @@ void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) {
wrapState.len2 = 0;
y = _verbLineY;
-
+
memset(wrapState.textBuffer, 0, sizeof(wrapState.textBuffer));
-
+
for (int16 i = 0; i <= _verbLineNum; i++) {
wrapState.sourceString = _vm->_script->getSlotData(_verbLineItems[i].slotIndex) + _verbLineItems[i].slotOffset;
len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState);
@@ -331,19 +338,19 @@ void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) {
wrapState.sourceString++;
wrapState.len1 -= len;
wrapState.len2 = len + 1;
-
+
drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0], wrapState);
wrapState.destString = wrapState.textBuffer;
wrapState.width = 0;
len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState);
wrapState.len1 += len;
-
+
y += 9;
}
y += 9;
}
-
+
wrapState.len1 -= len;
wrapState.len2 = len;
@@ -463,13 +470,12 @@ void Screen::addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16
textRect->x = CLIP<int16>(x - width / 2, 0, 640);
item->lineCount++;
}
-
+
y += font.getHeight() - 1;
}
void Screen::addTalkTextItemsToRenderQueue() {
-
for (int16 i = 0; i <= _talkTextItemNum; i++) {
TalkTextItem *item = &_talkTextItems[i];
byte *text = _vm->_script->getSlotData(item->slotIndex) + item->slotOffset;
@@ -482,14 +488,15 @@ void Screen::addTalkTextItemsToRenderQueue() {
if (item->duration < 0)
item->duration = 0;
+ if (!_vm->_cfgText)
+ return;
+
for (byte j = 0; j < item->lineCount; j++) {
- _renderQueue->addText(item->lines[j].x, item->lines[j].y, item->color, _fontResIndexArray[item->fontNum],
- text, item->lines[j].length);
+ _renderQueue->addText(item->lines[j].x, item->lines[j].y, item->color,
+ _fontResIndexArray[item->fontNum], text, item->lines[j].length);
text += item->lines[j].length;
}
-
}
-
}
int16 Screen::getTalkTextDuration() {
@@ -559,7 +566,7 @@ int16 Screen::wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wra
Font font(_vm->_res->load(fontResIndex)->data);
int16 len = 0;
-
+
while (*wrapState.sourceString >= 0x20 && *wrapState.sourceString < 0xF0) {
byte ch = *wrapState.sourceString;
byte charWidth;
@@ -573,9 +580,9 @@ int16 Screen::wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wra
wrapState.width += charWidth;
*wrapState.destString++ = *wrapState.sourceString++;
}
-
+
return len;
-
+
}
void Screen::drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex, GuiTextWrapState &wrapState) {
@@ -765,7 +772,7 @@ void Screen::loadState(Common::ReadStream *in) {
_verbLineItems[i].slotIndex = in->readUint16LE();
_verbLineItems[i].slotOffset = in->readUint16LE();
}
-
+
// Load talk text items
_talkTextX = in->readUint16LE();
_talkTextY = in->readUint16LE();
@@ -786,7 +793,7 @@ void Screen::loadState(Common::ReadStream *in) {
_talkTextItems[i].lines[j].length = in->readUint16LE();
}
}
-
+
// Load GUI bitmap
{
byte *gui = _frontScreen + _vm->_cameraHeight * 640;
diff --git a/engines/toltecs/screen.h b/engines/toltecs/screen.h
index 988f59c840..788cde50c6 100644
--- a/engines/toltecs/screen.h
+++ b/engines/toltecs/screen.h
@@ -154,15 +154,15 @@ public:
~Screen();
void unpackRle(byte *source, byte *dest, uint16 width, uint16 height);
-
+
void loadMouseCursor(uint resIndex);
-
+
void drawGuiImage(int16 x, int16 y, uint resIndex);
-
+
void startShakeScreen(int16 shakeCounter);
void stopShakeScreen();
- void updateShakeScreen();
-
+ bool updateShakeScreen();
+
// Sprite list
void addStaticSprite(byte *spriteItem);
void addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode);
@@ -175,7 +175,7 @@ public:
// Verb line
void updateVerbLine(int16 slotIndex, int16 slotOffset);
-
+
// Talk text
void updateTalkText(int16 slotIndex, int16 slotOffset);
void addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item);
@@ -207,7 +207,7 @@ public:
int16 slotIndex;
int16 slotOffset;
};
-
+
struct Rect {
int16 x, y, width, height;
};
@@ -215,12 +215,13 @@ public:
ToltecsEngine *_vm;
byte *_frontScreen, *_backScreen;
-
+
uint _fontResIndexArray[10];
byte _fontColor1, _fontColor2;
// Screen shaking
bool _shakeActive;
+ uint32 _shakeTime;
int16 _shakeCounterInit, _shakeCounter;
int _shakePos;
@@ -229,7 +230,7 @@ public:
VerbLineItem _verbLineItems[8];
int16 _verbLineX, _verbLineY, _verbLineWidth;
int16 _verbLineCount;
-
+
// Talk text
int16 _talkTextX, _talkTextY;
int16 _talkTextMaxWidth;
diff --git a/engines/toltecs/script.cpp b/engines/toltecs/script.cpp
index 9683831980..5e8617bc43 100644
--- a/engines/toltecs/script.cpp
+++ b/engines/toltecs/script.cpp
@@ -183,10 +183,7 @@ void ScriptInterpreter::setMainScript(uint slotIndex) {
}
void ScriptInterpreter::runScript() {
- uint32 lastScreenUpdate = 0;
-
while (!_vm->shouldQuit()) {
-
if (_vm->_movieSceneFlag)
_vm->_mouseButton = 0;
@@ -197,7 +194,7 @@ void ScriptInterpreter::runScript() {
_vm->saveGameState(_vm->_saveLoadSlot, _vm->_saveLoadDescription);
_vm->_saveLoadRequested = 0;
}
-
+
if (_switchLocalDataNear) {
_switchLocalDataNear = false;
_localData = getSlotData(_regs.reg4);
@@ -214,20 +211,10 @@ void ScriptInterpreter::runScript() {
_localData = _stack + 2;
_switchLocalDataNear = true;
}
-
+
byte opcode = readByte();
execOpcode(opcode);
-
- // Update the screen at semi-regular intervals, else the mouse
- // cursor will be jerky.
- uint32 now = _vm->_system->getMillis();
- if (now < lastScreenUpdate || now - lastScreenUpdate > 10) {
- _vm->_system->updateScreen();
- lastScreenUpdate = _vm->_system->getMillis();
- }
-
}
-
}
byte ScriptInterpreter::readByte() {
@@ -547,7 +534,7 @@ const char *getVarName(uint variable) {
int16 ScriptInterpreter::getGameVar(uint variable) {
debug(0, "ScriptInterpreter::getGameVar(%d{%s})", variable, getVarName(variable));
-
+
switch (variable) {
case 0: return _vm->_mouseDisabled;
case 1: return _vm->_mouseY;
@@ -579,7 +566,7 @@ int16 ScriptInterpreter::getGameVar(uint variable) {
void ScriptInterpreter::setGameVar(uint variable, int16 value) {
debug(0, "ScriptInterpreter::setGameVar(%d{%s}, %d)", variable, getVarName(variable), value);
-
+
switch (variable) {
case 0:
_vm->_mouseDisabled = value;
@@ -718,7 +705,7 @@ void ScriptInterpreter::saveState(Common::WriteStream *out) {
// Save stack
out->write(_stack, kScriptStackSize);
out->writeUint16LE(_savedSp);
-
+
// Save IP
out->writeUint16LE((int16)(_code - getSlotData(_regs.reg4)));
@@ -1046,29 +1033,21 @@ void ScriptInterpreter::sfHandleInput() {
Only scancodes known to be used (so far) are converted
*/
switch (_vm->_keyState.keycode) {
- case Common::KEYCODE_ESCAPE:
+ case Common::KEYCODE_ESCAPE:
keyCode = 1;
break;
case Common::KEYCODE_F10:
keyCode = 68;
break;
default:
- break;
+ break;
}
}
localWrite16(varOfs, keyCode);
}
void ScriptInterpreter::sfRunOptionsScreen() {
- _vm->_screen->loadMouseCursor(12);
- _vm->_palette->loadAddPalette(9, 224);
- _vm->_palette->setDeltaPalette(_vm->_palette->getMainPalette(), 7, 0, 31, 224);
- _vm->_screen->finishTalkTextItems();
- _vm->_screen->clearSprites();
- CursorMan.showMouse(true);
- _vm->_menuSystem->run();
- _vm->_keyState.reset();
- _switchLocalDataNear = true;
+ _vm->showMenu(kMenuIdMain);
}
/* NOTE: The opcodes sfPrecacheSprites, sfPrecacheSounds1, sfPrecacheSounds2 and
@@ -1076,7 +1055,7 @@ void ScriptInterpreter::sfRunOptionsScreen() {
of data so the game doesn't stall while playing (due to the slow speed of
CD-Drives back then). This is not needed in ScummVM since all supported
systems are fast enough to load data in-game. */
-
+
void ScriptInterpreter::sfPrecacheSprites() {
// See note above
}
@@ -1102,7 +1081,9 @@ void ScriptInterpreter::sfSaveStackPtr() {
}
void ScriptInterpreter::sfPlayMovie() {
+ CursorMan.showMouse(false);
_vm->_moviePlayer->playMovie(arg16(3));
+ CursorMan.showMouse(true);
}
} // End of namespace Toltecs
diff --git a/engines/toltecs/script.h b/engines/toltecs/script.h
index 0c1898c525..89dca4598f 100644
--- a/engines/toltecs/script.h
+++ b/engines/toltecs/script.h
@@ -56,6 +56,8 @@ public:
void saveState(Common::WriteStream *out);
void loadState(Common::ReadStream *in);
+ void setSwitchLocalDataNear(bool newValue) { _switchLocalDataNear = newValue; }
+
protected:
struct ScriptRegs {
@@ -88,13 +90,13 @@ protected:
bool _cmpBitTest;
ScriptSlot _slots[kMaxScriptSlots];
-
+
ScriptRegs _regs;
int16 _savedSp;
byte readByte();
int16 readInt16();
-
+
void execOpcode(byte opcode);
void setupScriptFunctions();
diff --git a/engines/toltecs/segmap.cpp b/engines/toltecs/segmap.cpp
index f7d806c67b..b06c0af675 100644
--- a/engines/toltecs/segmap.cpp
+++ b/engines/toltecs/segmap.cpp
@@ -48,7 +48,7 @@ void SegmentMap::load(byte *source) {
uint16 maskRectCount = READ_LE_UINT16(source);
source += 2;
uint16 maskRectDataSize = maskRectCount * 12 + 2;
-
+
debug(0, "SegmentMap::load() maskRectCount = %d", maskRectCount);
for (uint16 i = 0; i < maskRectCount; i++) {
@@ -74,25 +74,25 @@ void SegmentMap::load(byte *source) {
// Load path rects
source += 2; // skip rects array size
-
+
uint16 pathRectCount = READ_LE_UINT16(source);
source += 2;
-
+
debug(0, "SegmentMap::load() pathRectCount = %d", pathRectCount);
-
+
for (uint16 i = 0; i < pathRectCount; i++) {
SegmapPathRect pathRect;
pathRect.y1 = READ_LE_UINT16(source);
pathRect.x1 = READ_LE_UINT16(source + 2);
pathRect.y2 = pathRect.y1 + READ_LE_UINT16(source + 4);
pathRect.x2 = pathRect.x1 + READ_LE_UINT16(source + 6);
-
+
debug(0, "SegmentMap::load() (%d, %d, %d, %d)", pathRect.x1, pathRect.y1, pathRect.x2, pathRect.y2);
source += 8;
_pathRects.push_back(pathRect);
}
-
+
// Load info rects
source += 2; // skip rects array size
@@ -141,7 +141,7 @@ void SegmentMap::adjustPathPoint(int16 &x, int16 &y) {
uint32 minDistance = 0xFFFFFFFF, distance;
int16 adjustedX = 0, adjustedY = 0, x2, y2;
-
+
for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
if (x >= _pathRects[rectIndex].x1 && x < _pathRects[rectIndex].x2) {
@@ -174,7 +174,7 @@ void SegmentMap::adjustPathPoint(int16 &x, int16 &y) {
}
}
-
+
x = adjustedX;
y = adjustedY;
@@ -318,7 +318,7 @@ void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 so
pointsArray[0] = 0;
pointsArray[1] = TO_LE_16(_pathNodesCount + 1);
}
-
+
debug(0, "SegmentMap::findPath() count = %d", FROM_LE_16(pointsArray[1]));
#if 0 // DEBUG: Draw the path we found
@@ -335,7 +335,7 @@ void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 so
sy = y;
}
#endif
-
+
}
int8 SegmentMap::getScalingAtPoint(int16 x, int16 y) {
diff --git a/engines/toltecs/segmap.h b/engines/toltecs/segmap.h
index 30182a6b71..dda0edeb88 100644
--- a/engines/toltecs/segmap.h
+++ b/engines/toltecs/segmap.h
@@ -61,14 +61,14 @@ public:
void getRgbModifiertAtPoint(int16 x, int16 y, int16 id, byte &r, byte &g, byte &b);
void addMasksToRenderQueue();
-
+
//protected:
public: // for debugging purposes
struct SegmapPathRect {
int16 x1, y1, x2, y2;
};
-
+
struct SegmapInfoRect {
int16 y, x;
int16 height, width;
@@ -78,11 +78,11 @@ public: // for debugging purposes
return py >= y && py <= y + height && px >= x && px <= x + width;
}
};
-
+
struct PathPoint {
int16 y, x;
};
-
+
typedef Common::Array<SegmapMaskRect> SegmapMaskRectArray;
typedef Common::Array<SegmapPathRect> SegmapPathRectArray;
typedef Common::Array<SegmapInfoRect> SegmapInfoRectArray;
diff --git a/engines/toltecs/sound.cpp b/engines/toltecs/sound.cpp
index c9ef00e31b..4b281392e5 100644
--- a/engines/toltecs/sound.cpp
+++ b/engines/toltecs/sound.cpp
@@ -34,48 +34,38 @@ namespace Toltecs {
Sound::Sound(ToltecsEngine *vm) : _vm(vm) {
for (int i = 0; i < kMaxChannels; i++) {
- channels[i].type = kChannelTypeEmpty;
- channels[i].resIndex = -1;
+ clearChannel(i);
}
}
Sound::~Sound() {
}
+void Sound::clearChannel(int channel) {
+ channels[channel].type = kChannelTypeEmpty;
+ channels[channel].resIndex = -1;
+ channels[channel].volume = 0;
+ channels[channel].panning = 0;
+}
+
void Sound::playSpeech(int16 resIndex) {
debug(0, "playSpeech(%d)", resIndex);
- internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0);
+
+ if (_vm->_cfgVoices)
+ internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0);
}
void Sound::playSound(int16 resIndex, int16 type, int16 volume) {
-
- // TODO: Use the right volumes
-
debug(0, "playSound(%d, %d, %d)", resIndex, type, volume);
-
- if (volume == -1 || type == -2) {
- if (type == kChannelTypeBackground) {
- internalPlaySound(resIndex, type, 50 /*TODO*/, 0);
- } else {
- internalPlaySound(resIndex, type, 100 /*TODO*/, 0);
- }
- } else {
- internalPlaySound(resIndex, type, 100 /*TODO*/, 0);
- }
+ internalPlaySound(resIndex, type, volume, 0);
}
void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) {
-
debug(0, "playSoundAtPos(%d, %d, %d)", resIndex, x, y);
- int16 volume, panning = 0, deltaX = 0;
- int8 scaling = _vm->_segmap->getScalingAtPoint(x, y);
-
- if (scaling >= 0)
- volume = 50 + ABS(scaling) / 2;
- else
- volume = 50 - ABS(scaling) / 2;
+ int16 volume = 50 + ABS(_vm->_segmap->getScalingAtPoint(x, y)) / 2;
+ int16 panning = 0, deltaX = 0;
if (_vm->_cameraX > x)
deltaX = _vm->_cameraX - x;
@@ -85,32 +75,31 @@ void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) {
deltaX = 600;
volume = ((100 - deltaX / 6) * volume) / 100;
-
+
if (_vm->_cameraX + 320 != x) {
panning = CLIP(x - (_vm->_cameraX + 320), -381, 381) / 3;
}
internalPlaySound(resIndex, 1, volume, panning);
-
}
void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning) {
+ // Change the game's sound volume (0 - 100) to Scummvm's scale (0 - 255)
+ volume = (volume == -1) ? 255 : volume * 255 / 100;
if (resIndex == -1) {
// Stop all sounds
_vm->_mixer->stopAll();
_vm->_screen->keepTalkTextItemsAlive();
for (int i = 0; i < kMaxChannels; i++) {
- channels[i].type = kChannelTypeEmpty;
- channels[i].resIndex = -1;
+ clearChannel(i);
}
} else if (type == -2) {
// Stop sounds with specified resIndex
for (int i = 0; i < kMaxChannels; i++) {
if (channels[i].resIndex == resIndex) {
_vm->_mixer->stopHandle(channels[i].handle);
- channels[i].type = kChannelTypeEmpty;
- channels[i].resIndex = -1;
+ clearChannel(i);
}
}
} else {
@@ -119,7 +108,7 @@ void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 pa
// Stop speech and play new sound
stopSpeech();
}
-
+
// Play new sound in empty channel
int freeChannel = -1;
for (int i = 0; i < kMaxChannels; i++) {
@@ -128,7 +117,7 @@ void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 pa
break;
}
}
-
+
// If all channels are in use no new sound will be played
if (freeChannel >= 0) {
Resource *soundResource = _vm->_res->load(resIndex);
@@ -141,19 +130,17 @@ void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 pa
channels[freeChannel].type = type;
channels[freeChannel].resIndex = resIndex;
+ channels[freeChannel].volume = volume;
+ channels[freeChannel].panning = panning;
- Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType;
- /*
- switch (type) {
- }
- */
+ Audio::Mixer::SoundType soundType = getScummVMSoundType((SoundChannelType)type);
_vm->_mixer->playStream(soundType, &channels[freeChannel].handle,
- stream, -1, volume, panning);
+ stream, -1, volume, panning);
}
}
-
+
}
void Sound::updateSpeech() {
@@ -170,8 +157,7 @@ void Sound::stopSpeech() {
if (channels[i].type == kChannelTypeSpeech) {
_vm->_mixer->stopHandle(channels[i].handle);
_vm->_screen->keepTalkTextItemsAlive();
- channels[i].type = kChannelTypeEmpty;
- channels[i].resIndex = -1;
+ clearChannel(i);
}
}
}
@@ -180,8 +166,7 @@ void Sound::stopAll() {
for (int i = 0; i < kMaxChannels; i++) {
_vm->_mixer->stopHandle(channels[i].handle);
_vm->_screen->keepTalkTextItemsAlive();
- channels[i].type = kChannelTypeEmpty;
- channels[i].resIndex = -1;
+ clearChannel(i);
}
}
@@ -189,13 +174,22 @@ void Sound::saveState(Common::WriteStream *out) {
for (int i = 0; i < kMaxChannels; i++) {
out->writeSint16LE(channels[i].type);
out->writeSint16LE(channels[i].resIndex);
+ out->writeSint16LE(channels[i].volume);
+ out->writeSint16LE(channels[i].panning);
}
}
-void Sound::loadState(Common::ReadStream *in) {
+void Sound::loadState(Common::ReadStream *in, int version) {
for (int i = 0; i < kMaxChannels; i++) {
channels[i].type = in->readSint16LE();
channels[i].resIndex = in->readSint16LE();
+ if (version < 4) {
+ channels[i].volume = (channels[i].type == kChannelTypeBackground) ? 50 : 100;
+ channels[i].panning = 0;
+ } else {
+ channels[i].volume = in->readSint16LE();
+ channels[i].panning = in->readSint16LE();
+ }
if (channels[i].type != kChannelTypeEmpty) {
Resource *soundResource = _vm->_res->load(channels[i].resIndex);
@@ -206,19 +200,26 @@ void Sound::loadState(Common::ReadStream *in) {
DisposeAfterUse::NO),
channels[i].type == kChannelTypeBackground ? 0 : 1);
- Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType;
- /*
- switch (type) {
- }
- */
-
- // TODO: Volume and panning
- int16 volume = (channels[i].type == kChannelTypeBackground) ? 50 : 100;
+ Audio::Mixer::SoundType soundType = getScummVMSoundType((SoundChannelType)channels[i].type);
_vm->_mixer->playStream(soundType, &channels[i].handle,
- stream, -1, volume, /*panning*/0);
+ stream, -1, channels[i].volume, channels[i].panning);
}
}
}
+Audio::Mixer::SoundType Sound::getScummVMSoundType(SoundChannelType type) const {
+ switch (type) {
+ case kChannelTypeBackground:
+ case kChannelTypeSfx:
+ return Audio::Mixer::kSFXSoundType;
+ case kChannelTypeSpeech:
+ return Audio::Mixer::kSpeechSoundType;
+ break;
+ default:
+ return Audio::Mixer::kSFXSoundType;
+ break;
+ }
+}
+
} // End of namespace Toltecs
diff --git a/engines/toltecs/sound.h b/engines/toltecs/sound.h
index e292d22c0f..48a6cd1318 100644
--- a/engines/toltecs/sound.h
+++ b/engines/toltecs/sound.h
@@ -42,6 +42,8 @@ enum SoundChannelType {
struct SoundChannel {
int16 resIndex;
int16 type;
+ int16 volume;
+ int16 panning;
Audio::SoundHandle handle;
};
@@ -60,15 +62,16 @@ public:
void stopAll();
void saveState(Common::WriteStream *out);
- void loadState(Common::ReadStream *in);
+ void loadState(Common::ReadStream *in, int version);
protected:
ToltecsEngine *_vm;
SoundChannel channels[kMaxChannels];
+ void clearChannel(int channel);
void internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning);
-
+ Audio::Mixer::SoundType getScummVMSoundType(SoundChannelType type) const;
};
diff --git a/engines/toltecs/sprite.cpp b/engines/toltecs/sprite.cpp
index 7a02663793..6101eb7d85 100644
--- a/engines/toltecs/sprite.cpp
+++ b/engines/toltecs/sprite.cpp
@@ -199,7 +199,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem
sprite.frameNum = frameNum;
spriteData = _vm->_res->load(drawRequest.resIndex)->data;
-
+
if (drawRequest.flags & 0x1000) {
sprite.flags |= 4;
}
@@ -207,7 +207,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem
if (drawRequest.flags & 0x2000) {
sprite.flags |= 0x10;
}
-
+
if (drawRequest.flags & 0x4000) {
sprite.flags |= 0x40;
}
@@ -218,7 +218,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem
if (spriteFrameEntry.w == 0 || spriteFrameEntry.h == 0)
return false;
-
+
sprite.offset = spriteFrameEntry.offset;
sprite.width = spriteFrameEntry.w;
@@ -263,12 +263,12 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem
xoffs -= (xoffs * scaleValue) / 100;
yoffs -= (yoffs * scaleValue) / 100;
}
-
+
}
-
+
sprite.x -= xoffs;
sprite.y -= yoffs;
-
+
sprite.yerror = sprite.ydelta;
// Now we check if the sprite needs to be clipped
@@ -283,7 +283,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem
sprite.height -= clipHeight;
if (sprite.height <= 0)
return false;
-
+
sprite.y = _vm->_cameraY;
// If the sprite is scaled
@@ -311,7 +311,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem
}
sprite.yerror = chopHeight;
}
-
+
spriteFrameData = spriteData + sprite.offset;
// Now the sprite's offset is adjusted to point to the starting line
if ((sprite.flags & 0x10) == 0) {
@@ -439,7 +439,7 @@ void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawIt
SpriteReaderStatus status;
PixelPacket packet;
-
+
byte *destp = dest;
int16 skipX = sprite.skipX;
@@ -459,7 +459,7 @@ void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawIt
status = reader.readPacket(packet);
}
}
-
+
if (w - packet.count < 0)
packet.count = w;
diff --git a/engines/toltecs/toltecs.cpp b/engines/toltecs/toltecs.cpp
index 6d6c37dffd..9f3a10a03b 100644
--- a/engines/toltecs/toltecs.cpp
+++ b/engines/toltecs/toltecs.cpp
@@ -62,11 +62,6 @@ struct GameSettings {
};
ToltecsEngine::ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
-
- // Setup mixer
- _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
-
_rnd = new Common::RandomSource("toltecs");
}
@@ -85,7 +80,7 @@ Common::Error ToltecsEngine::run() {
_flag01 = 0;
_saveLoadRequested = 0;
-
+
_cameraX = 0;
_cameraY = 0;
_newCameraX = 0;
@@ -96,7 +91,7 @@ Common::Error ToltecsEngine::run() {
_sceneWidth = 0;
_sceneHeight = 0;
-
+
_doSpeech = true;
_doText = true;
@@ -126,17 +121,27 @@ Common::Error ToltecsEngine::run() {
_moviePlayer = new MoviePlayer(this);
_music = new Music(_arc);
_menuSystem = new MenuSystem(this);
-
+
_sound = new Sound(this);
+ _cfgText = ConfMan.getBool("subtitles");
+ _cfgVoices = !ConfMan.getBool("speech_mute");
+
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mute ? 0 : ConfMan.getInt("speech_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, mute ? 0 : ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mute ? 0 : ConfMan.getInt("sfx_volume"));
syncSoundSettings();
CursorMan.showMouse(true);
setupSysStrings();
-//#define TEST_MENU
-#ifdef TEST_MENU
+#if 0
+ // Menu test
_screen->registerFont(0, 0x0D);
_screen->registerFont(1, 0x0E);
_screen->loadMouseCursor(12);
@@ -181,7 +186,7 @@ Common::Error ToltecsEngine::run() {
delete _music;
delete _moviePlayer;
delete _menuSystem;
-
+
delete _sound;
return Common::kNoError;
@@ -245,7 +250,7 @@ void ToltecsEngine::loadScene(uint resIndex) {
_screen->_fullRefresh = true;
_screen->_renderQueue->clear();
-
+
}
void ToltecsEngine::updateScreen() {
@@ -306,6 +311,7 @@ void ToltecsEngine::drawScreen() {
}
_system->updateScreen();
+ _system->delayMillis(10);
updateCamera();
}
@@ -321,15 +327,14 @@ void ToltecsEngine::updateInput() {
//debug("key: flags = %02X; keycode = %d", _keyState.flags, _keyState.keycode);
- // FIXME: This is just for debugging
switch (event.kbd.keycode) {
- case Common::KEYCODE_F7:
- savegame("toltecs.001", "Quicksave");
+ case Common::KEYCODE_F5:
+ showMenu(kMenuIdSave);
break;
- case Common::KEYCODE_F9:
- loadgame("toltecs.001");
+ case Common::KEYCODE_F7:
+ showMenu(kMenuIdLoad);
break;
- case Common::KEYCODE_ESCAPE:
+ case Common::KEYCODE_SPACE:
// Skip current dialog line, if a dialog is active
if (_screen->getTalkTextDuration() > 0) {
_sound->stopSpeech();
@@ -422,7 +427,7 @@ void ToltecsEngine::setCamera(int16 x, int16 y) {
_screen->finishTalkTextItems();
_screen->clearSprites();
-
+
_cameraX = x;
_newCameraX = x;
@@ -491,9 +496,9 @@ void ToltecsEngine::updateCamera() {
}
void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) {
-
+
byte *scanData = _script->getSlotData(slotIndex) + slotOffset;
-
+
while (*scanData < 0xF0) {
if (*scanData == 0x19) {
scanData++;
@@ -506,7 +511,7 @@ void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) {
}
scanData++;
}
-
+
if (*scanData == 0xFE) {
if (_doSpeech) {
int16 resIndex = READ_LE_UINT16(scanData + 1);
@@ -540,7 +545,7 @@ void ToltecsEngine::walk(byte *walkData) {
walkInfo.xerror = READ_LE_UINT16(walkData + 14);
walkInfo.mulValue = READ_LE_UINT16(walkData + 16);
walkInfo.scaling = READ_LE_UINT16(walkData + 18);
-
+
walkInfo.scaling = -_segmap->getScalingAtPoint(walkInfo.x, walkInfo.y);
if (walkInfo.y1 < walkInfo.y2)
@@ -548,7 +553,7 @@ void ToltecsEngine::walk(byte *walkData) {
else
ystep = 1;
ydelta = ABS(walkInfo.y1 - walkInfo.y2) * _walkSpeedY;
-
+
if (walkInfo.x1 < walkInfo.x2)
xstep = -1;
else
@@ -611,11 +616,11 @@ void ToltecsEngine::walk(byte *walkData) {
}
-int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
+int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
byte *rectDataEnd) {
rectData += index * itemSize;
-
+
while (rectData < rectDataEnd) {
int16 rectY = READ_LE_UINT16(rectData);
if (rectY == -10)
@@ -633,9 +638,32 @@ int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 ind
index++;
rectData += itemSize;
}
-
+
return -1;
+}
+
+void ToltecsEngine::showMenu(MenuID menuId) {
+ _screen->loadMouseCursor(12);
+ _palette->loadAddPalette(9, 224);
+ _palette->setDeltaPalette(_palette->getMainPalette(), 7, 0, 31, 224);
+ _screen->finishTalkTextItems();
+ _screen->clearSprites();
+ CursorMan.showMouse(true);
+ _menuSystem->run(menuId);
+ _keyState.reset();
+ _script->setSwitchLocalDataNear(true);
+}
+
+void ToltecsEngine::syncSoundSettings() {
+ Engine::syncSoundSettings();
+
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+ _cfgVoicesVolume = (mute ? 0 : ConfMan.getInt("speech_volume")) * 20 / Audio::Mixer::kMaxChannelVolume;
+ _cfgMusicVolume = (mute ? 0 : ConfMan.getInt("music_volume")) * 20 / Audio::Mixer::kMaxChannelVolume;
+ _cfgSoundFXVolume = (mute ? 0 : ConfMan.getInt("sfx_volume")) * 20 / Audio::Mixer::kMaxChannelVolume;
}
} // End of namespace Toltecs
diff --git a/engines/toltecs/toltecs.h b/engines/toltecs/toltecs.h
index efa1f9d13a..b95a4f77cb 100644
--- a/engines/toltecs/toltecs.h
+++ b/engines/toltecs/toltecs.h
@@ -81,6 +81,14 @@ enum SysString {
kSysStrCount
};
+enum MenuID {
+ kMenuIdNone,
+ kMenuIdMain,
+ kMenuIdSave,
+ kMenuIdLoad,
+ kMenuIdVolumes
+};
+
class ToltecsEngine : public ::Engine {
Common::KeyState _keyPressed;
@@ -99,6 +107,7 @@ public:
uint32 getFeatures() const;
Common::Language getLanguage() const;
const Common::String& getTargetName() const { return _targetName; }
+ void syncSoundSettings();
void setupSysStrings();
void requestSavegame(int slotNum, Common::String &description);
@@ -119,14 +128,18 @@ public:
void scrollCameraLeft(int16 delta);
void scrollCameraRight(int16 delta);
void updateCamera();
-
+
+ void showMenu(MenuID menuId);
+
void talk(int16 slotIndex, int16 slotOffset);
void walk(byte *walkData);
-
- int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
+
+ int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
byte *rectDataEnd);
+ int _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume;
+ bool _cfgText, _cfgVoices;
public:
AnimationPlayer *_anim;
@@ -150,7 +163,7 @@ public:
uint _sceneResIndex;
int16 _sceneWidth, _sceneHeight;
-
+
int _counter01, _counter02;
bool _movieSceneFlag;
byte _flag01;
@@ -161,7 +174,7 @@ public:
int16 _guiHeight;
bool _doSpeech, _doText;
-
+
int16 _walkSpeedY, _walkSpeedX;
Common::KeyState _keyState;
diff --git a/engines/tony/custom.cpp b/engines/tony/custom.cpp
new file mode 100644
index 0000000000..23c655e35a
--- /dev/null
+++ b/engines/tony/custom.cpp
@@ -0,0 +1,2530 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/system.h"
+#include "common/savefile.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/memory.h"
+#include "tony/custom.h"
+#include "tony/font.h"
+#include "tony/game.h"
+#include "tony/gfxcore.h"
+#include "tony/tony.h"
+#include "tony/tonychar.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+static const char *const kAmbianceFile[] = {
+ "None",
+ "1.ADP", // Grilli.WAV
+ "2.ADP", // Grilli-Ovattati.WAV
+ "3.ADP", // Grilli-Vento.WAV
+ "3.ADP", // Grilli-Vento1.WAV
+ "5.ADP", // Vento1.WAV
+ "4.ADP", // Mare1.WAV
+ "6.ADP" // Mare1.WAV half volume
+};
+
+static const MusicFileEntry kMusicFiles[] = {
+ {"00.ADP", 0}, {"01.ADP", 0}, {"02.ADP", 0}, {"03.ADP", 0},
+ {"04.ADP", 0}, {"05.ADP", 0}, {"06.ADP", 0}, {"07.ADP", 0},
+ {"08.ADP", 2450}, {"09.ADP", 0}, {"10.ADP", 0}, {"11.ADP", 0},
+ {"12.ADP", 0}, {"13.ADP", 0}, {"14.ADP", 0}, {"15.ADP", 0},
+ {"16.ADP", 0}, {"17.ADP", 0}, {"18.ADP", 0}, {"19.ADP", 0},
+ {"20.ADP", 0}, {"21.ADP", 0}, {"22.ADP", 0}, {"23.ADP", 0},
+ {"24.ADP", 0}, {"25.ADP", 0}, {"26.ADP", 0}, {"27.ADP", 0},
+ {"28.ADP", 1670}, {"29.ADP", 0}, {"30.ADP", 0}, {"31.ADP", 0},
+ {"32.ADP", 2900}, {"33.ADP", 0}, {"34.ADP", 0}, {"35.ADP", 0},
+ {"36.ADP", 0}, {"37.ADP", 0}, {"38.ADP", 0}, {"39.ADP", 0},
+ {"40.ADP", 0}, {"41.ADP", 1920}, {"42.ADP", 1560}, {"43.ADP", 1920},
+ {"44.ADP", 1920}, {"45.ADP", 1920}, {"46.ADP", 1920}, {"47.ADP", 1920},
+ {"48.ADP", 1920}, {"49.ADP", 1920}, {"50.ADP", 1920}, {"51.ADP", 1920},
+ {"52.ADP", 1920}, {"53.ADP", 0}, {"54.ADP", 0}, {"55.ADP", 0},
+ {"56.ADP", 0}, {"57.ADP", 0}, {"58.ADP", 0}, {"59.ADP", 0}
+};
+
+
+static const char *const kJingleFileNames[] = {
+ "S00.ADP", "S01.ADP", "S02.ADP", "S03.ADP", "S04.ADP",
+ "S05.ADP", "S06.ADP", "S07.ADP", "S08.ADP", "S09.ADP",
+ "S10.ADP", "S11.ADP", "S12.ADP", "S13.ADP", "S14.ADP",
+ "S15.ADP", "S16.ADP", "S17.ADP", "S18.ADP"
+};
+
+void reapplyChangedHotspot() {
+ for (int i = 0; i < GLOBALS._curChangedHotspot; i++)
+ GLOBALS._loc->getItemFromCode(GLOBALS._changedHotspot[i]._dwCode)->changeHotspot(RMPoint(GLOBALS._changedHotspot[i]._nX, GLOBALS._changedHotspot[i]._nY));
+}
+
+void saveChangedHotspot(Common::OutSaveFile *f) {
+ f->writeByte(GLOBALS._curChangedHotspot);
+ if (GLOBALS._curChangedHotspot > 0) {
+ for (int i = 0; i < GLOBALS._curChangedHotspot; ++i)
+ GLOBALS._changedHotspot[i].save(f);
+ }
+}
+
+void loadChangedHotspot(Common::InSaveFile *f) {
+ GLOBALS._curChangedHotspot = f->readByte();
+
+ if (GLOBALS._curChangedHotspot > 0) {
+ for (int i = 0; i < GLOBALS._curChangedHotspot; ++i)
+ GLOBALS._changedHotspot[i].load(f);
+ }
+}
+
+/**
+ * Classes required for custom functions
+ *
+ * Tony (To Move him) -> You can do MPAL through the animation? I really think so
+ *
+ * SendMessage -> I'd say just theEngine.SendMessage()
+ * ChangeLocation -> theEngine.ChangeLocation()
+ * AddInventory -> theEngine.AddInventory()
+*/
+
+void mCharResetCodes() {
+ for (int i = 0; i < 10; i++)
+ GLOBALS._mCharacter[i]._item = GLOBALS._loc->getItemFromCode(GLOBALS._mCharacter[i]._code);
+ for (int i = 0; i < 10; i++)
+ GLOBALS._character[i]._item = GLOBALS._loc->getItemFromCode(GLOBALS._character[i]._code);
+}
+
+void charsSaveAll(Common::OutSaveFile *f) {
+ for (int i = 0; i < 10; i++) {
+ f->writeByte(GLOBALS._isMChar[i]);
+ if (GLOBALS._isMChar[i]) {
+ GLOBALS._mCharacter[i].save(f);
+ } else {
+ GLOBALS._character[i].save(f);
+ }
+ }
+}
+
+void charsLoadAll(Common::InSaveFile *f) {
+ for (int i = 0; i < 10; i++) {
+ GLOBALS._isMChar[i] = f->readByte();
+ if (GLOBALS._isMChar[i])
+ GLOBALS._mCharacter[i].load(f);
+ else
+ GLOBALS._character[i].load(f);
+ }
+}
+
+void faceToMe(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDDOWN);
+}
+
+void backToMe(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDUP);
+}
+
+void leftToMe(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDLEFT);
+}
+
+void rightToMe(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDRIGHT);
+}
+
+
+void tonySetPerorate(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) {
+ g_vm->getEngine()->setPerorate(bStatus);
+}
+
+void mySleep(CORO_PARAM, uint32 dwTime, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_1(CoroScheduler.sleep, dwTime);
+
+ CORO_END_CODE;
+}
+
+void setAlwaysDisplay(CORO_PARAM, uint32 val, uint32, uint32, uint32) {
+ GLOBALS._bAlwaysDisplay = (val != 0);
+}
+
+
+void setPointer(CORO_PARAM, uint32 dwPointer, uint32, uint32, uint32) {
+ switch (dwPointer) {
+ case 1:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWUP);
+ break;
+ case 2:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWDOWN);
+ break;
+ case 3:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWLEFT);
+ break;
+ case 4:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWRIGHT);
+ break;
+ case 5:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWMAP);
+ break;
+
+ default:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_NONE);
+ break;
+ }
+}
+
+VoiceHeader *searchVoiceHeader(uint32 codehi, uint32 codelo) {
+ int code = (codehi << 16) | codelo;
+
+ if (g_vm->_voices.size() == 0)
+ return NULL;
+
+ for (uint i = 0; i < g_vm->_voices.size(); i++) {
+ if (g_vm->_voices[i]._code == code)
+ return &g_vm->_voices[i];
+ }
+
+ return NULL;
+}
+
+
+void sendTonyMessage(CORO_PARAM, uint32 dwMessage, uint32 nX, uint32 nY, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage msg;
+ int i;
+ int curOffset;
+ VoiceHeader *curVoc;
+ FPSfx *voice;
+ RMTextDialog text;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->curOffset = 0;
+
+ if (GLOBALS._bSkipIdle)
+ return;
+
+ _ctx->msg.load(dwMessage);
+ if (!_ctx->msg.isValid())
+ return;
+
+ _ctx->curVoc = searchVoiceHeader(0, dwMessage);
+ _ctx->voice = NULL;
+ if (_ctx->curVoc) {
+ // Is positioned within the database of entries beginning at the first
+ _ctx->curOffset = _ctx->curVoc->_offset;
+
+ // First time allocation
+ g_vm->_vdbFP.seek(_ctx->curOffset);
+ g_vm->_theSound.createSfx(&_ctx->voice);
+
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+ _ctx->curOffset = g_vm->_vdbFP.pos();
+
+ _ctx->voice->setLoop(false);
+ }
+
+ if (GLOBALS._nTonyNextTalkType != GLOBALS._tony->TALK_NORMAL) {
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType);
+
+ if (!GLOBALS._bStaticTalk)
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+ } else {
+ if (_ctx->msg.numPeriods() > 1)
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_HIPS);
+ else
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_NORMAL);
+ }
+
+ if (GLOBALS._curBackText)
+ CORO_INVOKE_0(GLOBALS._curBackText->hide);
+
+ GLOBALS._bTonyIsSpeaking = true;
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg.numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) {
+ _ctx->text.setInput(GLOBALS._input);
+
+ // Alignment
+ _ctx->text.setAlignType(RMText::HCENTER, RMText::VBOTTOM);
+
+ // Color
+ _ctx->text.setColor(0, 255, 0);
+
+ // Writes the text
+ _ctx->text.writeText(_ctx->msg[_ctx->i], 0);
+
+ // Set the position
+ if (nX == 0 && nY == 0)
+ _ctx->text.setPosition(GLOBALS._tony->position() - RMPoint(0, 130) - GLOBALS._loc->scrollPosition());
+ else
+ _ctx->text.setPosition(RMPoint(nX, nY) - GLOBALS._loc->scrollPosition());
+
+ // Handling for always display
+ if (GLOBALS._bAlwaysDisplay) {
+ _ctx->text.setAlwaysDisplay();
+ _ctx->text.forceTime();
+ }
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(&_ctx->text);
+
+ if (_ctx->curVoc) {
+ if (_ctx->i == 0) {
+ _ctx->voice->play();
+ _ctx->text.setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ } else {
+ g_vm->_vdbFP.seek(_ctx->curOffset);
+ g_vm->_theSound.createSfx(&_ctx->voice);
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+
+ _ctx->curOffset = g_vm->_vdbFP.pos();
+ _ctx->voice->setLoop(false);
+ _ctx->voice->play();
+ _ctx->text.setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ }
+ }
+
+ // Wait for the end of the display
+ _ctx->text.setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text.waitForEndDisplay);
+
+ if (_ctx->curVoc) {
+ _ctx->voice->stop();
+ _ctx->voice->release();
+ _ctx->voice = NULL;
+ }
+ }
+
+ GLOBALS._bTonyIsSpeaking = false;
+ if (GLOBALS._curBackText)
+ GLOBALS._curBackText->show();
+
+ CORO_INVOKE_0(GLOBALS._tony->endTalk);
+
+ CORO_END_CODE;
+}
+
+void changeBoxStatus(CORO_PARAM, uint32 nLoc, uint32 nBox, uint32 nStatus, uint32) {
+ GLOBALS._boxes->changeBoxStatus(nLoc, nBox, nStatus);
+}
+
+
+void custLoadLocation(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) {
+ CORO_BEGIN_CONTEXT;
+ uint32 h;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._curChangedHotspot = 0;
+ if (bUseStartPos != 0)
+ g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), GLOBALS._startLocPos[nLoc]);
+ else
+ g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), RMPoint(-1, -1));
+
+ _ctx->h = mpalQueryDoAction(0, nLoc, 0);
+
+ // On Enter?
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+
+void sendFullscreenMsgStart(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage *msg;
+ RMGfxClearTask clear;
+ int i;
+ RMTextDialog text;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->msg = new RMMessage(nMsg);
+
+ GLOBALS._fullScreenMessageLoc = GLOBALS._loc->TEMPGetNumLoc();
+ GLOBALS._fullScreenMessagePt = GLOBALS._tony->position();
+
+ if (GLOBALS._bSkipIdle)
+ return;
+
+ CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, false, NULL);
+ GLOBALS._tony->hide();
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) {
+ _ctx->text.setInput(GLOBALS._input);
+
+ // Alignment
+ _ctx->text.setAlignType(RMText::HCENTER, RMText::VCENTER);
+
+ // Forces the text to disappear in time
+ _ctx->text.forceTime();
+
+ // Color
+ _ctx->text.setColor(255, 255, 255);
+
+ // Write the text
+ if (nFont == 0)
+ _ctx->text.writeText((*_ctx->msg)[_ctx->i], 1);
+ else if (nFont == 1)
+ _ctx->text.writeText((*_ctx->msg)[_ctx->i], 0);
+
+ // Set the position
+ _ctx->text.setPosition(RMPoint(320, 240));
+
+ _ctx->text.setAlwaysDisplay();
+ _ctx->text.forceTime();
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(&_ctx->clear);
+ g_vm->getEngine()->linkGraphicTask(&_ctx->text);
+
+ // Wait for the end of display
+ _ctx->text.setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text.waitForEndDisplay);
+ }
+
+ delete _ctx->msg;
+
+ CORO_END_CODE;
+}
+
+void clearScreen(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ char buf[256];
+ RMGfxClearTask clear;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ g_vm->getEngine()->linkGraphicTask(&_ctx->clear);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ // WORKAROUND: This fixes a bug in the original source where the linked clear task
+ // didn't have time to be drawn and removed from the draw list before the method
+ // ended, thus remaining in the draw list and causing a later crash
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+void sendFullscreenMsgEnd(CORO_PARAM, uint32 bNotEnableTony, uint32, uint32, uint32) {
+ g_vm->getEngine()->loadLocation(GLOBALS._fullScreenMessageLoc, RMPoint(GLOBALS._fullScreenMessagePt._x, GLOBALS._fullScreenMessagePt._y), RMPoint(-1, -1));
+ if (!bNotEnableTony)
+ GLOBALS._tony->show();
+
+ mCharResetCodes();
+ reapplyChangedHotspot();
+}
+
+
+void sendFullscreenMessage(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(sendFullscreenMsgStart, nMsg, nFont, 0, 0);
+ CORO_INVOKE_4(sendFullscreenMsgEnd, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+void noBullsEye(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bNoBullsEye = true;
+}
+
+void closeLocation(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!GLOBALS._bNoBullsEye) {
+ g_vm->getEngine()->initWipe(1);
+ CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd);
+ }
+
+ g_vm->stopMusic(4);
+
+ // On exit, unload
+ CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, true, NULL);
+
+ CORO_END_CODE;
+}
+
+
+void changeLocation(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) {
+ CORO_BEGIN_CONTEXT;
+ uint32 h;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!GLOBALS._bNoBullsEye) {
+ g_vm->getEngine()->initWipe(1);
+ CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd);
+ }
+
+ if (GLOBALS._lastTappeto != GLOBALS._ambiance[nLoc]) {
+ g_vm->stopMusic(4);
+ }
+
+ // On exit, unfreeze
+ CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, true, NULL);
+
+ GLOBALS._curChangedHotspot = 0;
+ if (bUseStartPos != 0)
+ g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), GLOBALS._startLocPos[nLoc]);
+ else
+ g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), RMPoint(-1, -1));
+
+ if (GLOBALS._lastTappeto != GLOBALS._ambiance[nLoc]) {
+ GLOBALS._lastTappeto = GLOBALS._ambiance[nLoc];
+ if (GLOBALS._lastTappeto != 0)
+ g_vm->playMusic(4, kAmbianceFile[GLOBALS._lastTappeto], 0, true, 2000);
+ }
+
+ if (!GLOBALS._bNoBullsEye) {
+ g_vm->getEngine()->initWipe(2);
+ }
+
+ _ctx->h = mpalQueryDoAction(0, nLoc, 0);
+
+ if (!GLOBALS._bNoBullsEye) {
+ CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd);
+ g_vm->getEngine()->closeWipe();
+ }
+
+ GLOBALS._bNoBullsEye = false;
+
+ // On Enter?
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+void setLocStartPosition(CORO_PARAM, uint32 nLoc, uint32 lX, uint32 lY, uint32) {
+ GLOBALS._startLocPos[nLoc].set(lX, lY);
+}
+
+void saveTonyPosition(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._saveTonyPos = GLOBALS._tony->position();
+ GLOBALS._saveTonyLoc = GLOBALS._loc->TEMPGetNumLoc();
+}
+
+void restoreTonyPosition(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(changeLocation, GLOBALS._saveTonyLoc, GLOBALS._saveTonyPos._x, GLOBALS._saveTonyPos._y, 0);
+
+ mCharResetCodes();
+
+ CORO_END_CODE;
+}
+
+void disableInput(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->getEngine()->disableInput();
+}
+
+void enableInput(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->getEngine()->enableInput();
+}
+
+void stopTony(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->stopNoAction(coroParam);
+}
+
+void custEnableGUI(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS.EnableGUI();
+}
+
+void custDisableGUI(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS.DisableGUI();
+}
+
+void tonyGenericTake1(CORO_PARAM, uint32 nDirection) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->take(nDirection, 0);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ CORO_END_CODE;
+}
+
+void tonyGenericTake2(CORO_PARAM, uint32 nDirection) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->take(nDirection, 1);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ GLOBALS._tony->take(nDirection, 2);
+
+ CORO_END_CODE;
+}
+
+void tonyGenericPut1(CORO_PARAM, uint32 nDirection) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->put(nDirection, 0);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ CORO_END_CODE;
+}
+
+void tonyGenericPut2(CORO_PARAM, uint32 nDirection) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->put(nDirection, 1);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ GLOBALS._tony->put(nDirection, 2);
+
+ CORO_END_CODE;
+}
+
+
+void tonyTakeUp1(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericTake1(coroParam, 0);
+}
+
+
+void tonyTakeMid1(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericTake1(coroParam, 1);
+}
+
+void tonyTakeDown1(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericTake1(coroParam, 2);
+}
+
+void tonyTakeUp2(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericTake2(coroParam, 0);
+}
+
+
+void tonyTakeMid2(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericTake2(coroParam, 1);
+}
+
+void tonyTakeDown2(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericTake2(coroParam, 2);
+}
+
+void tonyPutUp1(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericPut1(coroParam, 0);
+}
+
+void tonyPutMid1(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericPut1(coroParam, 1);
+}
+
+void tonyPutDown1(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericPut1(coroParam, 2);
+}
+
+void tonyPutUp2(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericPut2(coroParam, 0);
+}
+
+void tonyPutMid2(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericPut2(coroParam, 1);
+}
+
+void tonyPutDown2(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ tonyGenericPut2(coroParam, 2);
+}
+
+
+void tonyOnTheFloor(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) {
+ if (dwParte == 0)
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_ONTHEFLOORLEFT);
+ else
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_ONTHEFLOORRIGHT);
+}
+
+void tonyGetUp(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (dwParte == 0)
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_GETUPLEFT);
+ else
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_GETUPRIGHT);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ CORO_END_CODE;
+}
+
+void tonyShepherdess(CORO_PARAM, uint32 bIsPast, uint32, uint32, uint32) {
+ GLOBALS._tony->setShepherdess(bIsPast);
+}
+
+void tonyWhistle(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WHISTLERIGHT);
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDRIGHT);
+
+ CORO_END_CODE;
+}
+
+void tonySetNumTexts(uint32 dwText) {
+ GLOBALS._dwTonyNumTexts = dwText;
+ GLOBALS._bTonyInTexts = false;
+}
+
+void tonyLaugh(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_LAUGH;
+}
+
+void tonyGiggle(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_LAUGH2;
+}
+
+void tonyHips(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_HIPS;
+}
+
+void tonySing(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SING;
+}
+
+void tonyIndicate(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_INDICATE;
+}
+
+void tonyScaredWithHands(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCARED;
+}
+
+void tonyScaredWithoutHands(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCARED2;
+}
+
+void tonyWithHammer(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHHAMMER;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHHAMMER);
+}
+
+void tonyWithGlasses(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHGLASSES;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHGLASSES);
+}
+
+void tonyWithWorm(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHWORM;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHWORM);
+}
+
+void tonyWithRope(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHROPE;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHROPE);
+}
+
+void tonyWithSecretary(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSECRETARY;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHSECRETARY);
+}
+
+void tonyWithRabbitANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRABBIT;
+}
+
+void tonyWithRecipeANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRECIPE;
+}
+
+void tonyWithCardsANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHCARDS;
+}
+
+void tonyWithSnowmanANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSNOWMAN;
+}
+
+void tonyWithSnowmanStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSNOWMANSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHSNOWMANSTATIC);
+
+ CORO_END_CODE;
+}
+
+void tonyWithSnowmanEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHSNOWMANSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+void tonyWithRabbitStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRABBITSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHRABBITSTATIC);
+
+ CORO_END_CODE;
+}
+
+void tonyWithRabbitEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHRABBITSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+void tonyWithRecipeStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRECIPESTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHRECIPESTATIC);
+
+ CORO_END_CODE;
+}
+
+void tonyWithRecipeEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHRECIPESTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+void tonyWithCardsStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHCARDSSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHCARDSSTATIC);
+
+ CORO_END_CODE;
+}
+
+void tonyWithCardsEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHCARDSSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+void tonyWithNotebookStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITH_NOTEBOOK;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITH_NOTEBOOK);
+
+ CORO_END_CODE;
+}
+
+void tonyWithNotebookEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITH_NOTEBOOK);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+void tonyWithMegaphoneStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHMEGAPHONESTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHMEGAPHONESTATIC);
+
+ CORO_END_CODE;
+}
+
+void tonyWithMegaphoneEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHMEGAPHONESTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+void tonyWithBeardStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHBEARDSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHBEARDSTATIC);
+
+ CORO_END_CODE;
+}
+
+void tonyWithBeardEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHBEARDSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+void tonyScaredStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCAREDSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_SCAREDSTATIC);
+
+ CORO_END_CODE;
+}
+
+void tonyScaredEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_SCAREDSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+
+void tonyDisgusted(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_DISGUSTED;
+}
+
+void tonySniffLeft(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_SNIFF_LEFT);
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+ CORO_INVOKE_4(leftToMe, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+void tonySniffRight(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_SNIFF_RIGHT);
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+ CORO_INVOKE_4(rightToMe, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+void tonySarcastic(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ tonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SARCASTIC;
+}
+
+void tonyMacbeth(CORO_PARAM, uint32 nPos, uint32, uint32, uint32) {
+ switch (nPos) {
+ case 1:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH1;
+ break;
+ case 2:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH2;
+ break;
+ case 3:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH3;
+ break;
+ case 4:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH4;
+ break;
+ case 5:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH5;
+ break;
+ case 6:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH6;
+ break;
+ case 7:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH7;
+ break;
+ case 8:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH8;
+ break;
+ case 9:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH9;
+ break;
+ }
+}
+
+
+void enableTony(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->show();
+}
+
+void disableTony(CORO_PARAM, uint32 bShowShadow, uint32, uint32, uint32) {
+ GLOBALS._tony->hide(bShowShadow);
+}
+
+void waitForPatternEnd(CORO_PARAM, uint32 nItem, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMItem *item;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->item = GLOBALS._loc->getItemFromCode(nItem);
+
+ if (!GLOBALS._bSkipIdle && _ctx->item != NULL)
+ CORO_INVOKE_1(_ctx->item->waitForEndPattern, GLOBALS._hSkipIdle);
+
+ CORO_END_CODE;
+}
+
+
+void setTonyPosition(CORO_PARAM, uint32 nX, uint32 nY, uint32 nLoc, uint32) {
+ GLOBALS._tony->setPosition(RMPoint(nX, nY), nLoc);
+}
+
+void moveTonyAndWait(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // WORKAROUND: Delay for a frame before starting the move to give any previous move time to finish.
+ // This fixes a bug in the first scene where if you immediately 'Use Door', Tony moves to the door,
+ // and then floats to the right rather than properly walking.
+ CORO_SLEEP(1);
+
+ CORO_INVOKE_1(GLOBALS._tony->move, RMPoint(nX, nY));
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndMovement);
+
+ CORO_END_CODE;
+}
+
+void moveTony(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) {
+ GLOBALS._tony->move(coroParam, RMPoint(nX, nY));
+}
+
+void scrollLocation(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) {
+ CORO_BEGIN_CONTEXT;
+ int lx, ly;
+ RMPoint pt;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Take the scroll coordinates
+ _ctx->lx = (int32)nX;
+ _ctx->ly = (int32)nY;
+
+ _ctx->pt = GLOBALS._loc->scrollPosition();
+
+ while ((_ctx->lx != 0 || _ctx->ly != 0) && !GLOBALS._bSkipIdle) {
+ if (_ctx->lx > 0) {
+ _ctx->lx -= (int32)sX;
+ if (_ctx->lx < 0)
+ _ctx->lx = 0;
+ _ctx->pt.offset((int32)sX, 0);
+ } else if (_ctx->lx < 0) {
+ _ctx->lx += (int32)sX;
+ if (_ctx->lx > 0)
+ _ctx->lx = 0;
+ _ctx->pt.offset(-(int32)sX, 0);
+ }
+
+ if (_ctx->ly > 0) {
+ _ctx->ly -= sY;
+ if (_ctx->ly < 0)
+ _ctx->ly = 0;
+ _ctx->pt.offset(0, sY);
+ } else if (_ctx->ly < 0) {
+ _ctx->ly += sY;
+ if (_ctx->ly > 0)
+ _ctx->ly = 0;
+ _ctx->pt.offset(0, -(int32)sY);
+ }
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ GLOBALS._loc->setScrollPosition(_ctx->pt);
+ GLOBALS._tony->setScrollPosition(_ctx->pt);
+ }
+
+ CORO_END_CODE;
+}
+
+void syncScrollLocation(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) {
+ CORO_BEGIN_CONTEXT;
+ int lx, ly;
+ RMPoint pt, startpt;
+ uint32 dwStartTime, dwCurTime, dwTotalTime;
+ uint32 stepX, stepY;
+ int dimx, dimy;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Take the scroll coordinates
+ _ctx->lx = (int32)nX;
+ _ctx->ly = (int32)nY;
+ _ctx->dimx = _ctx->lx;
+ _ctx->dimy = _ctx->ly;
+ if (_ctx->lx < 0)
+ _ctx->dimx = -_ctx->lx;
+
+ if (_ctx->ly < 0)
+ _ctx->dimy = -_ctx->ly;
+
+ _ctx->stepX = sX;
+ _ctx->stepY = sY;
+
+ _ctx->startpt = GLOBALS._loc->scrollPosition();
+
+ _ctx->dwStartTime = g_vm->getTime();
+
+ if (sX)
+ _ctx->dwTotalTime = _ctx->dimx * (1000 / 35) / sX;
+ else
+ _ctx->dwTotalTime = _ctx->dimy * (1000 / 35) / sY;
+
+ while ((_ctx->lx != 0 || _ctx->ly != 0) && !GLOBALS._bSkipIdle) {
+ _ctx->dwCurTime = g_vm->getTime() - _ctx->dwStartTime;
+ if (_ctx->dwCurTime > _ctx->dwTotalTime)
+ break;
+
+ _ctx->pt = _ctx->startpt;
+
+ if (sX) {
+ if (_ctx->lx > 0)
+ _ctx->pt._x += (_ctx->dimx * _ctx->dwCurTime) / _ctx->dwTotalTime;
+ else
+ _ctx->pt._x -= (_ctx->dimx * _ctx->dwCurTime) / _ctx->dwTotalTime;
+ } else {
+ if (_ctx->ly > 0)
+ _ctx->pt._y += (_ctx->dimy * _ctx->dwCurTime) / _ctx->dwTotalTime;
+ else
+ _ctx->pt._y -= (_ctx->dimy * _ctx->dwCurTime) / _ctx->dwTotalTime;
+
+ }
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ GLOBALS._loc->setScrollPosition(_ctx->pt);
+ GLOBALS._tony->setScrollPosition(_ctx->pt);
+
+ }
+
+
+ // Set the position finale
+ if (sX) {
+ if (_ctx->lx > 0)
+ _ctx->pt._x = _ctx->startpt._x + _ctx->dimx;
+ else
+ _ctx->pt._x = _ctx->startpt._x - _ctx->dimx;
+ } else {
+ if (_ctx->ly > 0)
+ _ctx->pt._y = _ctx->startpt._y + _ctx->dimy;
+ else
+ _ctx->pt._y = _ctx->startpt._y - _ctx->dimy;
+ }
+
+ GLOBALS._loc->setScrollPosition(_ctx->pt);
+ GLOBALS._tony->setScrollPosition(_ctx->pt);
+
+ CORO_END_CODE;
+}
+
+
+void changeHotspot(CORO_PARAM, uint32 dwCode, uint32 nX, uint32 nY, uint32) {
+ int i;
+
+ for (i = 0; i < GLOBALS._curChangedHotspot; i++) {
+ if (GLOBALS._changedHotspot[i]._dwCode == dwCode) {
+ GLOBALS._changedHotspot[i]._nX = nX;
+ GLOBALS._changedHotspot[i]._nY = nY;
+ break;
+ }
+ }
+
+ if (i == GLOBALS._curChangedHotspot) {
+ GLOBALS._changedHotspot[i]._dwCode = dwCode;
+ GLOBALS._changedHotspot[i]._nX = nX;
+ GLOBALS._changedHotspot[i]._nY = nY;
+ GLOBALS._curChangedHotspot++;
+ }
+
+ GLOBALS._loc->getItemFromCode(dwCode)->changeHotspot(RMPoint(nX, nY));
+}
+
+
+void autoSave(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->autoSave(coroParam);
+}
+
+void abortGame(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ error("script called abortGame");
+}
+
+void shakeScreen(CORO_PARAM, uint32 nScosse, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ uint32 i;
+ uint32 curTime;
+ int dirx, diry;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->curTime = g_vm->getTime();
+
+ _ctx->dirx = 1;
+ _ctx->diry = 1;
+
+ while (g_vm->getTime() < _ctx->curTime + nScosse) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ GLOBALS._loc->setFixedScroll(RMPoint(1 * _ctx->dirx, 1 * _ctx->diry));
+ GLOBALS._tony->setFixedScroll(RMPoint(1 * _ctx->dirx, 1 * _ctx->diry));
+
+ _ctx->i = g_vm->_randomSource.getRandomNumber(2);
+
+ if (_ctx->i == 0 || _ctx->i == 2)
+ _ctx->dirx = -_ctx->dirx;
+ else if (_ctx->i == 1 || _ctx->i == 2)
+ _ctx->diry = -_ctx->diry;
+ }
+
+ GLOBALS._loc->setFixedScroll(RMPoint(0, 0));
+ GLOBALS._tony->setFixedScroll(RMPoint(0, 0));
+
+ CORO_END_CODE;
+}
+
+/*
+ * Characters
+ */
+
+void charSetCode(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) {
+ assert(nChar < 16);
+ GLOBALS._character[nChar]._code = nCode;
+ GLOBALS._character[nChar]._item = GLOBALS._loc->getItemFromCode(nCode);
+ GLOBALS._character[nChar]._r = 255;
+ GLOBALS._character[nChar]._g = 255;
+ GLOBALS._character[nChar]._b = 255;
+ GLOBALS._character[nChar]._talkPattern = 0;
+ GLOBALS._character[nChar]._startTalkPattern = 0;
+ GLOBALS._character[nChar]._endTalkPattern = 0;
+ GLOBALS._character[nChar]._standPattern = 0;
+
+ GLOBALS._isMChar[nChar] = false;
+}
+
+void charSetColor(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) {
+ assert(nChar < 16);
+ GLOBALS._character[nChar]._r = r;
+ GLOBALS._character[nChar]._g = g;
+ GLOBALS._character[nChar]._b = b;
+}
+
+void charSetTalkPattern(CORO_PARAM, uint32 nChar, uint32 tp, uint32 sp, uint32) {
+ assert(nChar < 16);
+ GLOBALS._character[nChar]._talkPattern = tp;
+ GLOBALS._character[nChar]._standPattern = sp;
+}
+
+void charSetStartEndTalkPattern(CORO_PARAM, uint32 nChar, uint32 sp, uint32 ep, uint32) {
+ assert(nChar < 16);
+ GLOBALS._character[nChar]._startTalkPattern = sp;
+ GLOBALS._character[nChar]._endTalkPattern = ep;
+}
+
+void charSendMessage(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage *msg;
+ int i;
+ RMPoint pt;
+ RMTextDialog *text;
+ int curOffset;
+ VoiceHeader *curVoc;
+ FPSfx *voice;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->msg = new RMMessage(dwMessage);
+ _ctx->curOffset = 0;
+
+ assert(nChar < 16);
+ _ctx->pt = GLOBALS._character[nChar]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition();
+
+ if (GLOBALS._character[nChar]._startTalkPattern != 0) {
+ GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._startTalkPattern);
+
+ CORO_INVOKE_0(GLOBALS._character[nChar]._item->waitForEndPattern);
+ }
+
+ GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._talkPattern);
+
+ _ctx->curVoc = searchVoiceHeader(0, dwMessage);
+ _ctx->voice = NULL;
+ if (_ctx->curVoc) {
+ // Position within the database of entries, beginning at the first
+ g_vm->_vdbFP.seek(_ctx->curVoc->_offset);
+ _ctx->curOffset = _ctx->curVoc->_offset;
+ }
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) {
+ if (bIsBack) {
+ GLOBALS._curBackText = _ctx->text = new RMTextDialogScrolling(GLOBALS._loc);
+ if (GLOBALS._bTonyIsSpeaking)
+ CORO_INVOKE_0(GLOBALS._curBackText->hide);
+ } else
+ _ctx->text = new RMTextDialog;
+
+ _ctx->text->setInput(GLOBALS._input);
+
+ // Skipping
+ _ctx->text->setSkipStatus(!bIsBack);
+
+ // Alignment
+ _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM);
+
+ // Color
+ _ctx->text->setColor(GLOBALS._character[nChar]._r, GLOBALS._character[nChar]._g, GLOBALS._character[nChar]._b);
+
+ // Write the text
+ _ctx->text->writeText((*_ctx->msg)[_ctx->i], 0);
+
+ // Set the position
+ _ctx->text->setPosition(_ctx->pt);
+
+ // Set the always display
+ if (GLOBALS._bAlwaysDisplay) {
+ _ctx->text->setAlwaysDisplay();
+ _ctx->text->forceTime();
+ }
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(_ctx->text);
+
+ if (_ctx->curVoc) {
+ g_vm->_theSound.createSfx(&_ctx->voice);
+ g_vm->_vdbFP.seek(_ctx->curOffset);
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+ _ctx->voice->setLoop(false);
+ if (bIsBack)
+ _ctx->voice->setVolume(55);
+ _ctx->voice->play();
+ _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ _ctx->curOffset = g_vm->_vdbFP.pos();
+ }
+
+ // Wait for the end of display
+ _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text->waitForEndDisplay);
+
+ if (_ctx->curVoc) {
+ _ctx->voice->stop();
+ _ctx->voice->release();
+ _ctx->voice = NULL;
+ }
+
+
+ GLOBALS._curBackText = NULL;
+ delete _ctx->text;
+ }
+
+ if (GLOBALS._character[nChar]._endTalkPattern != 0) {
+ GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._endTalkPattern);
+ CORO_INVOKE_0(GLOBALS._character[nChar]._item->waitForEndPattern);
+ }
+
+ GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._standPattern);
+ delete _ctx->msg;
+
+ CORO_END_CODE;
+}
+
+void addInventory(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) {
+ GLOBALS._inventory->addItem(dwCode);
+}
+
+void removeInventory(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) {
+ GLOBALS._inventory->removeItem(dwCode);
+}
+
+void changeInventoryStatus(CORO_PARAM, uint32 dwCode, uint32 dwStatus, uint32, uint32) {
+ GLOBALS._inventory->changeItemStatus(dwCode, dwStatus);
+}
+
+
+/*
+ * Master Characters
+ */
+
+void mCharSetCode(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) {
+ assert(nChar < 10);
+ GLOBALS._mCharacter[nChar]._code = nCode;
+ if (nCode == 0)
+ GLOBALS._mCharacter[nChar]._item = NULL;
+ else
+ GLOBALS._mCharacter[nChar]._item = GLOBALS._loc->getItemFromCode(nCode);
+ GLOBALS._mCharacter[nChar]._r = 255;
+ GLOBALS._mCharacter[nChar]._g = 255;
+ GLOBALS._mCharacter[nChar]._b = 255;
+ GLOBALS._mCharacter[nChar]._x = -1;
+ GLOBALS._mCharacter[nChar]._y = -1;
+ GLOBALS._mCharacter[nChar]._bAlwaysBack = 0;
+
+ for (int i = 0; i < 10; i++)
+ GLOBALS._mCharacter[nChar]._numTalks[i] = 1;
+
+ GLOBALS._mCharacter[nChar]._curGroup = 0;
+
+ GLOBALS._isMChar[nChar] = true;
+}
+
+void mCharResetCode(CORO_PARAM, uint32 nChar, uint32, uint32, uint32) {
+ GLOBALS._mCharacter[nChar]._item = GLOBALS._loc->getItemFromCode(GLOBALS._mCharacter[nChar]._code);
+}
+
+
+void mCharSetPosition(CORO_PARAM, uint32 nChar, uint32 nX, uint32 nY, uint32) {
+ assert(nChar < 10);
+ GLOBALS._mCharacter[nChar]._x = nX;
+ GLOBALS._mCharacter[nChar]._y = nY;
+}
+
+void mCharSetColor(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) {
+ assert(nChar < 10);
+ GLOBALS._mCharacter[nChar]._r = r;
+ GLOBALS._mCharacter[nChar]._g = g;
+ GLOBALS._mCharacter[nChar]._b = b;
+}
+
+void mCharSetNumTalksInGroup(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32 nTalks, uint32) {
+ assert(nChar < 10);
+ assert(nGroup < 10);
+
+ GLOBALS._mCharacter[nChar]._numTalks[nGroup] = nTalks;
+}
+
+void mCharSetCurrentGroup(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32, uint32) {
+ assert(nChar < 10);
+ assert(nGroup < 10);
+
+ GLOBALS._mCharacter[nChar]._curGroup = nGroup;
+}
+
+void mCharSetNumTexts(CORO_PARAM, uint32 nChar, uint32 nTexts, uint32, uint32) {
+ assert(nChar < 10);
+
+ GLOBALS._mCharacter[nChar]._numTexts = nTexts - 1;
+ GLOBALS._mCharacter[nChar]._bInTexts = false;
+}
+
+void mCharSetAlwaysBack(CORO_PARAM, uint32 nChar, uint32 bAlwaysBack, uint32, uint32) {
+ assert(nChar < 10);
+
+ GLOBALS._mCharacter[nChar]._bAlwaysBack = bAlwaysBack;
+}
+
+void mCharSendMessage(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32 nFont) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage *msg;
+ int i;
+ int parm;
+ RMPoint pt;
+ uint32 h;
+ RMTextDialog *text;
+ int curOffset;
+ VoiceHeader *curVoc;
+ FPSfx *voice;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->msg = new RMMessage(dwMessage);
+ _ctx->curOffset = 0;
+
+ assert(nChar < 10);
+
+ bIsBack |= GLOBALS._mCharacter[nChar]._bAlwaysBack ? 1 : 0;
+
+ // Calculates the position of the text according to the current frame
+ if (GLOBALS._mCharacter[nChar]._x == -1)
+ _ctx->pt = GLOBALS._mCharacter[nChar]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition();
+ else
+ _ctx->pt = RMPoint(GLOBALS._mCharacter[nChar]._x, GLOBALS._mCharacter[nChar]._y);
+
+ // Parameter for special actions: random between the spoken
+ _ctx->parm = (GLOBALS._mCharacter[nChar]._curGroup * 10) + g_vm->_randomSource.getRandomNumber(
+ GLOBALS._mCharacter[nChar]._numTalks[GLOBALS._mCharacter[nChar]._curGroup] - 1) + 1;
+
+ // Try to run the custom function to initialize the speech
+ if (GLOBALS._mCharacter[nChar]._item) {
+ _ctx->h = mpalQueryDoAction(30, GLOBALS._mCharacter[nChar]._item->mpalCode(), _ctx->parm);
+ if (_ctx->h != CORO_INVALID_PID_VALUE) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+ }
+ }
+
+ _ctx->curVoc = searchVoiceHeader(0, dwMessage);
+ _ctx->voice = NULL;
+ if (_ctx->curVoc) {
+ // Position within the database of entries, beginning at the first
+ g_vm->_vdbFP.seek(_ctx->curVoc->_offset);
+ _ctx->curOffset = _ctx->curVoc->_offset;
+ }
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) {
+ // Create a different object depending on whether it's background or not
+ if (bIsBack) {
+ GLOBALS._curBackText = _ctx->text = new RMTextDialogScrolling(GLOBALS._loc);
+ if (GLOBALS._bTonyIsSpeaking)
+ CORO_INVOKE_0(GLOBALS._curBackText->hide);
+ } else
+ _ctx->text = new RMTextDialog;
+
+ _ctx->text->setInput(GLOBALS._input);
+
+ // Skipping
+ _ctx->text->setSkipStatus(!bIsBack);
+
+ // Alignment
+ _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM);
+
+ // Color
+ _ctx->text->setColor(GLOBALS._mCharacter[nChar]._r, GLOBALS._mCharacter[nChar]._g, GLOBALS._mCharacter[nChar]._b);
+
+ // Write the text
+ _ctx->text->writeText((*_ctx->msg)[_ctx->i], nFont);
+
+ // Set the position
+ _ctx->text->setPosition(_ctx->pt);
+
+ // Set the always display
+ if (GLOBALS._bAlwaysDisplay) {
+ _ctx->text->setAlwaysDisplay();
+ _ctx->text->forceTime();
+ }
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(_ctx->text);
+
+ if (_ctx->curVoc) {
+ g_vm->_theSound.createSfx(&_ctx->voice);
+ g_vm->_vdbFP.seek(_ctx->curOffset);
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+ _ctx->voice->setLoop(false);
+ if (bIsBack)
+ _ctx->voice->setVolume(55);
+ _ctx->voice->play();
+ _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ _ctx->curOffset = g_vm->_vdbFP.pos();
+ }
+
+ // Wait for the end of display
+ _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text->waitForEndDisplay);
+
+ if (_ctx->curVoc) {
+ _ctx->voice->stop();
+ _ctx->voice->release();
+ _ctx->voice = NULL;
+ }
+
+ GLOBALS._curBackText = NULL;
+ delete _ctx->text;
+ }
+
+ delete _ctx->msg;
+
+ // Try to run the custom function to close the speech
+ if (GLOBALS._mCharacter[nChar]._item) {
+ _ctx->h = mpalQueryDoAction(31, GLOBALS._mCharacter[nChar]._item->mpalCode(), _ctx->parm);
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+ }
+
+ CORO_END_CODE;
+}
+
+/*
+ * Dialogs
+ */
+
+void sendDialogMessage(CORO_PARAM, uint32 nPers, uint32 nMsg, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ char *string;
+ RMTextDialog *text;
+ int parm;
+ uint32 h;
+ bool bIsBack;
+ VoiceHeader *curVoc;
+ FPSfx *voice;
+ RMPoint pt;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bIsBack = false;
+
+ // The SendDialogMessage can go in the background if it is a character
+ if (nPers != 0 && GLOBALS._isMChar[nPers] && GLOBALS._mCharacter[nPers]._bAlwaysBack)
+ _ctx->bIsBack = true;
+
+ _ctx->curVoc = searchVoiceHeader(GLOBALS._curDialog, nMsg);
+ _ctx->voice = NULL;
+
+ if (_ctx->curVoc) {
+ // Position within the database of entries, beginning at the first
+ g_vm->_vdbFP.seek(_ctx->curVoc->_offset);
+ g_vm->_theSound.createSfx(&_ctx->voice);
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+ _ctx->voice->setLoop(false);
+ if (_ctx->bIsBack)
+ _ctx->voice->setVolume(55);
+ }
+
+ _ctx->string = mpalQueryDialogPeriod(nMsg);
+
+ if (nPers == 0) {
+ _ctx->text = new RMTextDialog;
+ _ctx->text->setColor(0, 255, 0);
+ _ctx->text->setPosition(GLOBALS._tony->position() - RMPoint(0, 130) - GLOBALS._loc->scrollPosition());
+ _ctx->text->writeText(_ctx->string, 0);
+
+ if (GLOBALS._dwTonyNumTexts > 0) {
+ if (!GLOBALS._bTonyInTexts) {
+ if (GLOBALS._nTonyNextTalkType != GLOBALS._tony->TALK_NORMAL) {
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType);
+ if (!GLOBALS._bStaticTalk)
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+ } else
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_NORMAL);
+
+ GLOBALS._bTonyInTexts = true;
+ }
+ GLOBALS._dwTonyNumTexts--;
+ } else {
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType);
+ if (!GLOBALS._bStaticTalk)
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+ }
+ } else if (!GLOBALS._isMChar[nPers]) {
+ _ctx->text = new RMTextDialog;
+
+ _ctx->pt = GLOBALS._character[nPers]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition();
+
+ if (GLOBALS._character[nPers]._startTalkPattern != 0) {
+ GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._startTalkPattern);
+ CORO_INVOKE_0(GLOBALS._character[nPers]._item->waitForEndPattern);
+ }
+
+ GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._talkPattern);
+
+ _ctx->text->setColor(GLOBALS._character[nPers]._r, GLOBALS._character[nPers]._g, GLOBALS._character[nPers]._b);
+ _ctx->text->writeText(_ctx->string, 0);
+ _ctx->text->setPosition(_ctx->pt);
+ } else {
+ if (GLOBALS._mCharacter[nPers]._x == -1)
+ _ctx->pt = GLOBALS._mCharacter[nPers]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition();
+ else
+ _ctx->pt = RMPoint(GLOBALS._mCharacter[nPers]._x, GLOBALS._mCharacter[nPers]._y);
+
+ // Parameter for special actions. Random between the spoken.
+ _ctx->parm = (GLOBALS._mCharacter[nPers]._curGroup * 10) + g_vm->_randomSource.getRandomNumber(
+ GLOBALS._mCharacter[nPers]._numTalks[GLOBALS._mCharacter[nPers]._curGroup] - 1) + 1;
+
+ if (GLOBALS._mCharacter[nPers]._numTexts != 0 && GLOBALS._mCharacter[nPers]._bInTexts) {
+ GLOBALS._mCharacter[nPers]._numTexts--;
+ } else {
+ // Try to run the custom function to initialize the speech
+ _ctx->h = mpalQueryDoAction(30, GLOBALS._mCharacter[nPers]._item->mpalCode(), _ctx->parm);
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+
+ GLOBALS._mCharacter[nPers]._curTalk = _ctx->parm;
+
+ if (GLOBALS._mCharacter[nPers]._numTexts != 0) {
+ GLOBALS._mCharacter[nPers]._bInTexts = true;
+ GLOBALS._mCharacter[nPers]._numTexts--;
+ }
+ }
+
+ if (GLOBALS._mCharacter[nPers]._bAlwaysBack) {
+ _ctx->text = GLOBALS._curBackText = new RMTextDialogScrolling(GLOBALS._loc);
+ if (GLOBALS._bTonyIsSpeaking)
+ CORO_INVOKE_0(GLOBALS._curBackText->hide);
+
+ _ctx->bIsBack = true;
+ } else
+ _ctx->text = new RMTextDialog;
+
+ _ctx->text->setSkipStatus(!GLOBALS._mCharacter[nPers]._bAlwaysBack);
+ _ctx->text->setColor(GLOBALS._mCharacter[nPers]._r, GLOBALS._mCharacter[nPers]._g, GLOBALS._mCharacter[nPers]._b);
+ _ctx->text->writeText(_ctx->string, 0);
+ _ctx->text->setPosition(_ctx->pt);
+ }
+
+ if (!GLOBALS._bSkipIdle) {
+ _ctx->text->setInput(GLOBALS._input);
+ if (GLOBALS._bAlwaysDisplay) {
+ _ctx->text->setAlwaysDisplay();
+ _ctx->text->forceTime();
+ }
+ _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM);
+ g_vm->getEngine()->linkGraphicTask(_ctx->text);
+
+ if (_ctx->curVoc) {
+ _ctx->voice->play();
+ _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ }
+
+ // Wait for the end of display
+ _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text->waitForEndDisplay);
+ }
+
+ if (_ctx->curVoc) {
+ _ctx->voice->stop();
+ _ctx->voice->release();
+ _ctx->voice = NULL;
+ }
+
+ if (nPers != 0) {
+ if (!GLOBALS._isMChar[nPers]) {
+ if (GLOBALS._character[nPers]._endTalkPattern != 0) {
+ GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._endTalkPattern);
+ CORO_INVOKE_0(GLOBALS._character[nPers]._item->waitForEndPattern);
+ }
+
+ GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._standPattern);
+ delete _ctx->text;
+ } else {
+ if ((GLOBALS._mCharacter[nPers]._bInTexts && GLOBALS._mCharacter[nPers]._numTexts == 0) || !GLOBALS._mCharacter[nPers]._bInTexts) {
+ // Try to run the custom function to close the speech
+ GLOBALS._mCharacter[nPers]._curTalk = (GLOBALS._mCharacter[nPers]._curTalk % 10) + GLOBALS._mCharacter[nPers]._curGroup * 10;
+ _ctx->h = mpalQueryDoAction(31, GLOBALS._mCharacter[nPers]._item->mpalCode(), GLOBALS._mCharacter[nPers]._curTalk);
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+
+ GLOBALS._mCharacter[nPers]._bInTexts = false;
+ GLOBALS._mCharacter[nPers]._numTexts = 0;
+ }
+
+ GLOBALS._curBackText = NULL;
+ delete _ctx->text;
+ }
+ } else {
+ if ((GLOBALS._dwTonyNumTexts == 0 && GLOBALS._bTonyInTexts) || !GLOBALS._bTonyInTexts) {
+ CORO_INVOKE_0(GLOBALS._tony->endTalk);
+ GLOBALS._dwTonyNumTexts = 0;
+ GLOBALS._bTonyInTexts = false;
+ }
+
+ delete _ctx->text;
+ }
+
+ globalDestroy(_ctx->string);
+
+ CORO_END_CODE;
+}
+
+
+// @@@@ This cannot be skipped!!!!!!!!!!!!!!!!!!!
+
+void startDialog(CORO_PARAM, uint32 nDialog, uint32 nStartGroup, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ uint32 nChoice;
+ uint32 *sl;
+ uint32 i, num;
+ char *string;
+ RMDialogChoice dc;
+ int sel;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._curDialog = nDialog;
+
+ // Call MPAL to start the dialog
+ mpalQueryDoDialog(nDialog, nStartGroup);
+
+ // Wait until a choice is selected
+ mpalQueryDialogWaitForChoice(&_ctx->nChoice);
+ while (_ctx->nChoice != (uint32) - 1) {
+ // Get the list of options
+ _ctx->sl = mpalQueryDialogSelectList(_ctx->nChoice);
+ for (_ctx->num = 0; _ctx->sl[_ctx->num] != 0; _ctx->num++)
+ ;
+
+ // If there is only one option, do it automatically, and wait for the next choice
+ if (_ctx->num == 1) {
+ mpalQueryDialogSelectionDWORD(_ctx->nChoice, _ctx->sl[0]);
+ globalDestroy(_ctx->sl);
+
+ // Wait for the next choice to be made
+ mpalQueryDialogWaitForChoice(&_ctx->nChoice);
+ continue;
+ }
+
+ // Making a choice for dialog
+ _ctx->dc.init();
+ _ctx->dc.setNumChoices(_ctx->num);
+
+ // Writeall the possible options
+ for (_ctx->i = 0; _ctx->i < _ctx->num; _ctx->i++) {
+ _ctx->string = mpalQueryDialogPeriod(_ctx->sl[_ctx->i]);
+ assert(_ctx->string != NULL);
+ _ctx->dc.addChoice(_ctx->string);
+ globalDestroy(_ctx->string);
+ }
+
+ // Activate the object
+ g_vm->getEngine()->linkGraphicTask(&_ctx->dc);
+ CORO_INVOKE_0(_ctx->dc.show);
+
+ // Draw the pointer
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_NONE);
+ g_vm->getEngine()->enableMouse();
+
+ while (!(GLOBALS._input->mouseLeftClicked() && ((_ctx->sel = _ctx->dc.getSelection()) != -1))) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ CORO_INVOKE_1(_ctx->dc.doFrame, GLOBALS._input->mousePos());
+ }
+
+ // Hide the pointer
+ g_vm->getEngine()->disableMouse();
+
+ CORO_INVOKE_0(_ctx->dc.hide);
+ mpalQueryDialogSelectionDWORD(_ctx->nChoice, _ctx->sl[_ctx->sel]);
+
+ // Closes the choice
+ _ctx->dc.close();
+
+ globalDestroy(_ctx->sl);
+
+ // Wait for the next choice to be made
+ mpalQueryDialogWaitForChoice(&_ctx->nChoice);
+ }
+
+ CORO_END_CODE;
+}
+
+
+/*
+ * Sync between idle and mpal
+ */
+
+void takeOwnership(CORO_PARAM, uint32 num, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (GLOBALS._mut[num]._ownerPid != (uint32)CoroScheduler.getCurrentPID()) {
+ // The mutex is currently owned by a different process.
+ // Wait for the event to be signalled, which means the mutex is free.
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._mut[num]._eventId, CORO_INFINITE);
+ GLOBALS._mut[num]._ownerPid = (uint32)CoroScheduler.getCurrentPID();
+ }
+
+ GLOBALS._mut[num]._lockCount++;
+
+ CORO_END_CODE;
+}
+
+void releaseOwnership(CORO_PARAM, uint32 num, uint32, uint32, uint32) {
+ if (!GLOBALS._mut[num]._lockCount) {
+ warning("ReleaseOwnership tried to release mutex %d, which isn't held", num);
+ return;
+ }
+
+ if (GLOBALS._mut[num]._ownerPid != (uint32)CoroScheduler.getCurrentPID()) {
+ warning("ReleaseOwnership tried to release mutex %d, which is held by a different process", num);
+ return;
+ }
+
+ GLOBALS._mut[num]._lockCount--;
+ if (!GLOBALS._mut[num]._lockCount) {
+ GLOBALS._mut[num]._ownerPid = 0;
+
+ // Signal the event, to wake up processes waiting for the lock.
+ CoroScheduler.setEvent(GLOBALS._mut[num]._eventId);
+ }
+}
+
+/*
+ * Music
+ * -----
+ *
+ * Fadeout effects supposed:
+ *
+ * nFX = 0 - The new music replaces the old one
+ * nFX=1 - The new music interfades with the old one
+ * nFX=2 - The new music takes over in time from the old
+ *
+ */
+
+void threadFadeInMusic(CORO_PARAM, const void *nMusic) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ int nChannel = *(const int *)nMusic;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "Start FadeIn Music");
+
+ for (_ctx->i = 0; _ctx->i < 16; _ctx->i++) {
+ g_vm->setMusicVolume(nChannel, _ctx->i * 4);
+
+ CORO_INVOKE_1(CoroScheduler.sleep, 100);
+ }
+ g_vm->setMusicVolume(nChannel, 64);
+
+ debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "End FadeIn Music");
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+void threadFadeOutMusic(CORO_PARAM, const void *nMusic) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ int startVolume;
+ CORO_END_CONTEXT(_ctx);
+
+ int nChannel = *(const int *)nMusic;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->startVolume = g_vm->getMusicVolume(nChannel);
+
+ for (_ctx->i = 16; _ctx->i > 0 && !GLOBALS._bFadeOutStop; _ctx->i--) {
+ if (_ctx->i * 4 < _ctx->startVolume)
+ g_vm->setMusicVolume(nChannel, _ctx->i * 4);
+
+ CORO_INVOKE_1(CoroScheduler.sleep, 100);
+ }
+
+ if (!GLOBALS._bFadeOutStop)
+ g_vm->setMusicVolume(nChannel, 0);
+
+ // If a jingle is played, stop it
+ if (nChannel == 2)
+ g_vm->stopMusic(2);
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+void fadeInSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CoroScheduler.createProcess(threadFadeInMusic, &GLOBALS._curSoundEffect, sizeof(int));
+}
+
+void fadeOutSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bFadeOutStop = false;
+ CoroScheduler.createProcess(threadFadeOutMusic, &GLOBALS._curSoundEffect, sizeof(int));
+}
+
+void fadeOutJingle(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bFadeOutStop = false;
+ int channel = 2;
+ CoroScheduler.createProcess(threadFadeOutMusic, &channel, sizeof(int));
+}
+
+void fadeInJingle(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ int channel = 2;
+ CoroScheduler.createProcess(threadFadeInMusic, &channel, sizeof(int));
+}
+
+void stopSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->stopMusic(GLOBALS._curSoundEffect);
+}
+
+void stopJingle(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->stopMusic(2);
+}
+
+void muteSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->setMusicVolume(GLOBALS._curSoundEffect, 0);
+}
+
+void demuteSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bFadeOutStop = true;
+ g_vm->setMusicVolume(GLOBALS._curSoundEffect, 64);
+}
+
+void muteJingle(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->setMusicVolume(2, 0);
+}
+
+void demuteJingle(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->setMusicVolume(2, 64);
+}
+
+void custPlayMusic(uint32 nChannel, const char *mFN, uint32 nFX, bool bLoop, int nSync = 0) {
+ if (nSync == 0)
+ nSync = 2000;
+ debugC(DEBUG_INTERMEDIATE, kTonyDebugMusic, "Start CustPlayMusic");
+ g_vm->playMusic(nChannel, mFN, nFX, bLoop, nSync);
+ debugC(DEBUG_INTERMEDIATE, kTonyDebugMusic, "End CustPlayMusic");
+}
+
+void playSoundEffect(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bNoLoop, uint32) {
+ if (nFX == 0 || nFX == 1 || nFX == 2) {
+ debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "PlaySoundEffect stop fadeout");
+ GLOBALS._bFadeOutStop = true;
+ }
+
+ GLOBALS._lastMusic = nMusic;
+ custPlayMusic(GLOBALS._curSoundEffect, kMusicFiles[nMusic]._name, nFX, bNoLoop ? false : true, kMusicFiles[nMusic]._sync);
+}
+
+void playJingle(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bLoop, uint32) {
+ custPlayMusic(2, kJingleFileNames[nMusic], nFX, bLoop);
+}
+
+void playItemSfx(CORO_PARAM, uint32 nItem, uint32 nSFX, uint32, uint32) {
+ if (nItem == 0) {
+ GLOBALS._tony->playSfx(nSFX);
+ } else {
+ RMItem *item = GLOBALS._loc->getItemFromCode(nItem);
+ if (item)
+ item->playSfx(nSFX);
+ }
+}
+
+void restoreMusic(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(playSoundEffect, GLOBALS._lastMusic, 0, 0, 0);
+
+ if (GLOBALS._lastTappeto != 0)
+ custPlayMusic(4, kAmbianceFile[GLOBALS._lastTappeto], 0, true);
+
+ CORO_END_CODE;
+}
+
+void saveMusic(Common::OutSaveFile *f) {
+ f->writeByte(GLOBALS._lastMusic);
+ f->writeByte(GLOBALS._lastTappeto);
+}
+
+void loadMusic(Common::InSaveFile *f) {
+ GLOBALS._lastMusic = f->readByte();
+ GLOBALS._lastTappeto = f->readByte();
+}
+
+void jingleFadeStart(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(fadeOutSoundEffect, 0, 0, 0, 0);
+ CORO_INVOKE_4(muteJingle, 0, 0, 0, 0);
+ CORO_INVOKE_4(playJingle, nJingle, 0, bLoop, 0);
+ CORO_INVOKE_4(fadeInJingle, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+void jingleFadeEnd(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(fadeOutJingle, 0, 0, 0, 0);
+ CORO_INVOKE_4(fadeInSoundEffect, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+void mustSkipIdleStart(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bSkipIdle = true;
+ CoroScheduler.setEvent(GLOBALS._hSkipIdle);
+}
+
+void mustSkipIdleEnd(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bSkipIdle = false;
+ CoroScheduler.resetEvent(GLOBALS._hSkipIdle);
+}
+
+void patIrqFreeze(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) {
+ // Unused in ScummVM.
+}
+
+void openInitLoadMenu(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_0(g_vm->openInitLoadMenu);
+
+ CORO_END_CODE;
+}
+
+void openInitOptions(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_0(g_vm->openInitOptions);
+
+ CORO_END_CODE;
+}
+
+void doCredits(CORO_PARAM, uint32 nMsg, uint32 dwTime, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage *msg;
+ RMTextDialog *text;
+ uint32 hDisable;
+ int i;
+ uint32 startTime;
+
+ ~CoroContextTag() {
+ delete msg;
+ delete[] text;
+ }
+
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->msg = new RMMessage(nMsg);
+ _ctx->hDisable = CoroScheduler.createEvent(true, false);
+
+ _ctx->text = new RMTextDialog[_ctx->msg->numPeriods()];
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods(); _ctx->i++) {
+ _ctx->text[_ctx->i].setInput(GLOBALS._input);
+
+ // Alignment
+ if ((*_ctx->msg)[_ctx->i][0] == '@') {
+ _ctx->text[_ctx->i].setAlignType(RMText::HCENTER, RMText::VTOP);
+ _ctx->text[_ctx->i].writeText(&(*_ctx->msg)[_ctx->i][1], 3);
+ _ctx->text[_ctx->i].setPosition(RMPoint(414, 70 + _ctx->i * 26)); // 70
+ } else {
+ _ctx->text[_ctx->i].setAlignType(RMText::HLEFT, RMText::VTOP);
+ _ctx->text[_ctx->i].writeText((*_ctx->msg)[_ctx->i], 3);
+ _ctx->text[_ctx->i].setPosition(RMPoint(260, 70 + _ctx->i * 26));
+ }
+
+
+ // Set the position
+ _ctx->text[_ctx->i].setAlwaysDisplay();
+ _ctx->text[_ctx->i].setForcedTime(dwTime * 1000);
+ _ctx->text[_ctx->i].setNoTab();
+
+ // Wait for the end of display
+ _ctx->text[_ctx->i].setCustomSkipHandle(_ctx->hDisable);
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(&_ctx->text[_ctx->i]);
+ }
+
+ _ctx->startTime = g_vm->getTime();
+
+ while (_ctx->startTime + dwTime * 1000 > g_vm->getTime()) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ if (GLOBALS._input->mouseLeftClicked() || GLOBALS._input->mouseRightClicked())
+ break;
+ if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_TAB))
+ break;
+ }
+
+ CoroScheduler.setEvent(_ctx->hDisable);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ delete[] _ctx->text;
+ delete _ctx->msg;
+ _ctx->text = NULL;
+ _ctx->msg = NULL;
+
+ CORO_END_CODE;
+}
+
+BEGIN_CUSTOM_FUNCTION_MAP()
+
+ASSIGN(1, custLoadLocation)
+ASSIGN(2, mySleep)
+ASSIGN(3, setPointer)
+ASSIGN(5, moveTony)
+ASSIGN(6, faceToMe)
+ASSIGN(7, backToMe)
+ASSIGN(8, leftToMe)
+ASSIGN(9, rightToMe)
+ASSIGN(10, sendTonyMessage)
+ASSIGN(11, changeBoxStatus)
+ASSIGN(12, changeLocation)
+ASSIGN(13, disableTony)
+ASSIGN(14, enableTony)
+ASSIGN(15, waitForPatternEnd)
+ASSIGN(16, setLocStartPosition)
+ASSIGN(17, scrollLocation)
+ASSIGN(18, moveTonyAndWait)
+ASSIGN(19, changeHotspot)
+ASSIGN(20, addInventory)
+ASSIGN(21, removeInventory)
+ASSIGN(22, changeInventoryStatus)
+ASSIGN(23, setTonyPosition)
+ASSIGN(24, sendFullscreenMessage)
+ASSIGN(25, saveTonyPosition)
+ASSIGN(26, restoreTonyPosition)
+ASSIGN(27, disableInput)
+ASSIGN(28, enableInput)
+ASSIGN(29, stopTony)
+
+ASSIGN(30, tonyTakeUp1)
+ASSIGN(31, tonyTakeMid1)
+ASSIGN(32, tonyTakeDown1)
+ASSIGN(33, tonyTakeUp2)
+ASSIGN(34, tonyTakeMid2)
+ASSIGN(35, tonyTakeDown2)
+
+ASSIGN(72, tonyPutUp1)
+ASSIGN(73, tonyPutMid1)
+ASSIGN(74, tonyPutDown1)
+ASSIGN(75, tonyPutUp2)
+ASSIGN(76, tonyPutMid2)
+ASSIGN(77, tonyPutDown2)
+
+ASSIGN(36, tonyOnTheFloor)
+ASSIGN(37, tonyGetUp)
+ASSIGN(38, tonyShepherdess)
+ASSIGN(39, tonyWhistle)
+
+ASSIGN(40, tonyLaugh)
+ASSIGN(41, tonyHips)
+ASSIGN(42, tonySing)
+ASSIGN(43, tonyIndicate)
+ASSIGN(44, tonyScaredWithHands)
+ASSIGN(49, tonyScaredWithoutHands)
+ASSIGN(45, tonyWithGlasses)
+ASSIGN(46, tonyWithWorm)
+ASSIGN(47, tonyWithHammer)
+ASSIGN(48, tonyWithRope)
+ASSIGN(90, tonyWithRabbitANIM)
+ASSIGN(91, tonyWithRecipeANIM)
+ASSIGN(92, tonyWithCardsANIM)
+ASSIGN(93, tonyWithSnowmanANIM)
+ASSIGN(94, tonyWithSnowmanStart)
+ASSIGN(95, tonyWithSnowmanEnd)
+ASSIGN(96, tonyWithRabbitStart)
+ASSIGN(97, tonyWithRabbitEnd)
+ASSIGN(98, tonyWithRecipeStart)
+ASSIGN(99, tonyWithRecipeEnd)
+ASSIGN(100, tonyWithCardsStart)
+ASSIGN(101, tonyWithCardsEnd)
+ASSIGN(102, tonyWithNotebookStart)
+ASSIGN(103, tonyWithNotebookEnd)
+ASSIGN(104, tonyWithMegaphoneStart)
+ASSIGN(105, tonyWithMegaphoneEnd)
+ASSIGN(106, tonyWithBeardStart)
+ASSIGN(107, tonyWithBeardEnd)
+ASSIGN(108, tonyGiggle)
+ASSIGN(109, tonyDisgusted)
+ASSIGN(110, tonySarcastic)
+ASSIGN(111, tonyMacbeth)
+ASSIGN(112, tonySniffLeft)
+ASSIGN(113, tonySniffRight)
+ASSIGN(114, tonyScaredStart)
+ASSIGN(115, tonyScaredEnd)
+ASSIGN(116, tonyWithSecretary)
+
+ASSIGN(50, charSetCode)
+ASSIGN(51, charSetColor)
+ASSIGN(52, charSetTalkPattern)
+ASSIGN(53, charSendMessage)
+ASSIGN(54, charSetStartEndTalkPattern)
+
+ASSIGN(60, mCharSetCode)
+ASSIGN(61, mCharSetColor)
+ASSIGN(62, mCharSetCurrentGroup)
+ASSIGN(63, mCharSetNumTalksInGroup)
+ASSIGN(64, mCharSetNumTexts)
+ASSIGN(65, mCharSendMessage)
+ASSIGN(66, mCharSetPosition)
+ASSIGN(67, mCharSetAlwaysBack)
+ASSIGN(68, mCharResetCode)
+
+ASSIGN(70, startDialog)
+ASSIGN(71, sendDialogMessage)
+
+ASSIGN(80, takeOwnership)
+ASSIGN(81, releaseOwnership)
+
+ASSIGN(86, playSoundEffect)
+ASSIGN(87, playJingle)
+ASSIGN(88, fadeInSoundEffect)
+ASSIGN(89, fadeOutSoundEffect)
+ASSIGN(123, fadeInJingle)
+ASSIGN(124, fadeOutJingle)
+ASSIGN(125, muteSoundEffect)
+ASSIGN(126, demuteSoundEffect)
+ASSIGN(127, muteJingle)
+ASSIGN(128, demuteJingle)
+ASSIGN(84, stopSoundEffect)
+ASSIGN(85, stopJingle)
+ASSIGN(83, playItemSfx)
+ASSIGN(129, jingleFadeStart)
+ASSIGN(130, jingleFadeEnd)
+
+ASSIGN(120, shakeScreen)
+ASSIGN(121, autoSave)
+ASSIGN(122, abortGame)
+ASSIGN(131, noBullsEye)
+ASSIGN(132, sendFullscreenMsgStart)
+ASSIGN(133, sendFullscreenMsgEnd)
+ASSIGN(134, custEnableGUI)
+ASSIGN(135, custDisableGUI)
+ASSIGN(136, clearScreen)
+ASSIGN(137, patIrqFreeze)
+ASSIGN(138, tonySetPerorate)
+ASSIGN(139, openInitLoadMenu)
+ASSIGN(140, openInitOptions)
+ASSIGN(141, syncScrollLocation)
+ASSIGN(142, closeLocation)
+ASSIGN(143, setAlwaysDisplay)
+ASSIGN(144, doCredits)
+
+ASSIGN(200, mustSkipIdleStart);
+ASSIGN(201, mustSkipIdleEnd);
+
+END_CUSTOM_FUNCTION_MAP()
+
+void processKilledCallback(Common::PROCESS *p) {
+ for (uint i = 0; i < 10; i++) {
+ if (GLOBALS._mut[i]._ownerPid == p->pid) {
+ // Handle scripts which don't call ReleaseOwnership, such as
+ // the one in loc37's vEnter when Tony is chasing the mouse.
+ debug(DEBUG_BASIC, "Force-releasing mutex %d after process died", i);
+
+ GLOBALS._mut[i]._ownerPid = 0;
+ GLOBALS._mut[i]._lockCount = 0;
+ CoroScheduler.setEvent(GLOBALS._mut[i]._eventId);
+ }
+ }
+}
+
+void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input) {
+ GLOBALS._tony = tony;
+ GLOBALS._pointer = ptr;
+ GLOBALS._boxes = box;
+ GLOBALS._loc = loc;
+ GLOBALS._inventory = inv;
+ GLOBALS._input = input;
+
+ GLOBALS.DisableGUI = mainDisableGUI;
+ GLOBALS.EnableGUI = mainEnableGUI;
+
+ GLOBALS._bAlwaysDisplay = false;
+
+ CoroScheduler.setResourceCallback(processKilledCallback);
+ for (int i = 0; i < 10; i++)
+ GLOBALS._mut[i]._eventId = CoroScheduler.createEvent(false, true);
+
+ for (int i = 0; i < 200; i++)
+ GLOBALS._ambiance[i] = 0;
+
+ GLOBALS._ambiance[6] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[7] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[8] = AMBIANCE_CRICKETSMUFFLED;
+ GLOBALS._ambiance[10] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[12] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[13] = AMBIANCE_CRICKETSMUFFLED;
+ GLOBALS._ambiance[15] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[16] = AMBIANCE_CRICKETSWIND;
+ GLOBALS._ambiance[18] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[19] = AMBIANCE_CRICKETSWIND;
+ GLOBALS._ambiance[20] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[23] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[26] = AMBIANCE_SEAHALFVOLUME;
+ GLOBALS._ambiance[27] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[28] = AMBIANCE_CRICKETSWIND;
+ GLOBALS._ambiance[31] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[33] = AMBIANCE_SEA;
+ GLOBALS._ambiance[35] = AMBIANCE_SEA;
+ GLOBALS._ambiance[36] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[37] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[40] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[41] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[42] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[45] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[51] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[52] = AMBIANCE_CRICKETSWIND1;
+ GLOBALS._ambiance[53] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[54] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[57] = AMBIANCE_WIND;
+ GLOBALS._ambiance[58] = AMBIANCE_WIND;
+ GLOBALS._ambiance[60] = AMBIANCE_WIND;
+
+
+
+ // Create an event for the idle skipping
+ GLOBALS._hSkipIdle = CoroScheduler.createEvent(true, false);
+}
+
+} // end of namespace Tony
diff --git a/engines/tony/custom.h b/engines/tony/custom.h
new file mode 100644
index 0000000000..0f1061e8cd
--- /dev/null
+++ b/engines/tony/custom.h
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_CUSTOM_H
+#define TONY_CUSTOM_H
+
+#include "common/str.h"
+#include "tony/mpal/mpal.h"
+
+namespace Tony {
+
+using namespace MPAL;
+
+struct MusicFileEntry {
+ const char *_name;
+ int _sync;
+};
+
+#define INIT_CUSTOM_FUNCTION MapCustomFunctions
+
+#define BEGIN_CUSTOM_FUNCTION_MAP() \
+ static void AssignError(int num) { \
+ error("Custom function %u has been already assigned!", num); \
+ } \
+ void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap) \
+ {
+
+#define END_CUSTOM_FUNCTION_MAP() \
+ }
+
+#define ASSIGN(num, func) \
+ if (lpMap[num] != NULL) \
+ AssignError(num); \
+ lpMap[num] = func; \
+ lpStrMap[num] = #func;
+
+class RMTony;
+class RMPointer;
+class RMGameBoxes;
+class RMLocation;
+class RMInventory;
+class RMInput;
+
+void charsSaveAll(Common::OutSaveFile *f);
+void charsLoadAll(Common::InSaveFile *f);
+void mCharResetCodes();
+void saveChangedHotspot(Common::OutSaveFile *f);
+void loadChangedHotspot(Common::InSaveFile *f);
+void reapplyChangedHotspot();
+
+void restoreMusic(CORO_PARAM);
+void saveMusic(Common::OutSaveFile *f);
+void loadMusic(Common::InSaveFile *f);
+
+void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap);
+void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input);
+
+#endif
+
+} // end of namespace Tony
diff --git a/engines/tony/debugger.cpp b/engines/tony/debugger.cpp
new file mode 100644
index 0000000000..85d9469519
--- /dev/null
+++ b/engines/tony/debugger.cpp
@@ -0,0 +1,130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/coroutines.h"
+#include "tony/debugger.h"
+#include "tony/globals.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+Debugger::Debugger() : GUI::Debugger() {
+ DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit));
+ DCmd_Register("scene", WRAP_METHOD(Debugger, Cmd_Scene));
+ DCmd_Register("dirty_rects", WRAP_METHOD(Debugger, Cmd_DirtyRects));
+}
+
+static int strToInt(const char *s) {
+ if (!*s)
+ // No string at all
+ return 0;
+ else if (toupper(s[strlen(s) - 1]) != 'H')
+ // Standard decimal string
+ return atoi(s);
+
+ // Hexadecimal string
+ uint tmp = 0;
+ int read = sscanf(s, "%xh", &tmp);
+ if (read < 1)
+ error("strToInt failed on string \"%s\"", s);
+ return (int)tmp;
+}
+
+/**
+ * Support process for changing the scene
+ */
+struct ChangeSceneDetails {
+ int sceneNumber;
+ int x;
+ int y;
+};
+
+void DebugChangeScene(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 result;
+ const ChangeSceneDetails *details = (const ChangeSceneDetails *)param;
+ RMPoint scenePos(details->x, details->y);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, false, &result);
+
+ g_vm->getEngine()->loadLocation(details->sceneNumber, scenePos, RMPoint(-1, -1));
+
+ mainEnableGUI();
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * This command loads up the specified new scene number
+ */
+bool Debugger::Cmd_Scene(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Usage: %s <scene number> [<x> <y>]\n", argv[0]);
+ return true;
+ }
+
+ int sceneNumber = strToInt(argv[1]);
+ if (sceneNumber >= g_vm->_theBoxes.getLocBoxesCount()) {
+ DebugPrintf("Invalid scene\n");
+ return true;
+ }
+
+ RMPoint scenePos;
+ if (argc >= 4) {
+ scenePos._x = strToInt(argv[2]);
+ scenePos._y = strToInt(argv[3]);
+ } else {
+ // Get the box areas for the scene, and choose one so as to have a default
+ // position for Tony that will be in the walkable areas
+ RMBoxLoc *box = g_vm->_theBoxes.getBoxes(sceneNumber);
+ scenePos.set(box->_boxes[0]._hotspot[0]._hotx, box->_boxes[0]._hotspot[0]._hoty);
+ }
+
+ // Set up a process to change the scene
+ ChangeSceneDetails details;
+ details.sceneNumber = sceneNumber;
+ details.x = scenePos._x;
+ details.y = scenePos._y;
+ CoroScheduler.createProcess(DebugChangeScene, &details, sizeof(ChangeSceneDetails));
+
+ return false;
+}
+
+/**
+ * Turns showing dirty rects on or off
+ */
+bool Debugger::Cmd_DirtyRects(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Usage; %s [on | off]\n", argv[0]);
+ return true;
+ } else {
+ g_vm->_window.showDirtyRects(strcmp(argv[1], "on") == 0);
+ return false;
+ }
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/debugger.h b/engines/tony/debugger.h
new file mode 100644
index 0000000000..85ba9d75b6
--- /dev/null
+++ b/engines/tony/debugger.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef TONY_DEBUGGER_H
+#define TONY_DEBUGGER_H
+
+#include "common/scummsys.h"
+#include "gui/debugger.h"
+
+namespace Tony {
+
+class Debugger : public GUI::Debugger {
+public:
+ Debugger();
+ virtual ~Debugger() {}
+
+protected:
+ bool Cmd_Scene(int argc, const char **argv);
+ bool Cmd_DirtyRects(int argc, const char **argv);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/detection.cpp b/engines/tony/detection.cpp
new file mode 100644
index 0000000000..8e6d5a64c3
--- /dev/null
+++ b/engines/tony/detection.cpp
@@ -0,0 +1,194 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#include "base/plugins.h"
+
+#include "common/memstream.h"
+#include "engines/advancedDetector.h"
+#include "common/system.h"
+#include "graphics/colormasks.h"
+#include "graphics/surface.h"
+
+#include "tony/tony.h"
+#include "tony/game.h"
+
+namespace Tony {
+
+enum {
+ GF_COMPRESSED = (1 << 0)
+};
+
+struct TonyGameDescription {
+ ADGameDescription desc;
+};
+
+uint32 TonyEngine::getFeatures() const {
+ return _gameDescription->desc.flags;
+}
+
+Common::Language TonyEngine::getLanguage() const {
+ return _gameDescription->desc.language;
+}
+
+bool TonyEngine::getIsDemo() const {
+ return _gameDescription->desc.flags & ADGF_DEMO;
+}
+
+bool TonyEngine::isCompressed() const {
+ return _gameDescription->desc.flags & GF_COMPRESSED;
+}
+
+} // End of namespace Tony
+
+static const PlainGameDescriptor tonyGames[] = {
+ {"tony", "Tony Tough and the Night of Roasted Moths"},
+ {0, 0}
+};
+
+#include "tony/detection_tables.h"
+
+class TonyMetaEngine : public AdvancedMetaEngine {
+public:
+ TonyMetaEngine() : AdvancedMetaEngine(Tony::gameDescriptions, sizeof(Tony::TonyGameDescription), tonyGames) {
+ }
+
+ virtual const char *getName() const {
+ return "Tony Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Tony Engine (C) Protonic Interactive";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual void removeSaveState(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+};
+
+bool TonyMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail);
+}
+
+bool Tony::TonyEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool TonyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const Tony::TonyGameDescription *gd = (const Tony::TonyGameDescription *)desc;
+ if (gd) {
+ *engine = new Tony::TonyEngine(syst, gd);
+ }
+ return gd != 0;
+}
+
+SaveStateList TonyMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String saveDesc;
+ Common::String pattern = "tony.0??";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ int slotNum = atoi(file->c_str() + file->size() - 3);
+
+ if (slotNum >= 0 && slotNum <= 999) {
+ byte thumbnailData[160 * 120 * 2];
+ Common::String saveName;
+ byte difficulty;
+
+ if (Tony::RMOptionScreen::loadThumbnailFromSaveState(slotNum, thumbnailData, saveName, difficulty)) {
+ // Add the save name to the savegame list
+ saveList.push_back(SaveStateDescriptor(slotNum, saveName));
+ }
+ }
+ }
+
+ return saveList;
+}
+
+int TonyMetaEngine::getMaximumSaveSlot() const {
+ return 99;
+}
+
+void TonyMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String filename = Tony::TonyEngine::getSaveStateFileName(slot);
+
+ g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+SaveStateDescriptor TonyMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String saveName;
+ byte difficulty;
+ byte thumbData[160 * 120 * 2];
+
+ if (Tony::RMOptionScreen::loadThumbnailFromSaveState(slot, thumbData, saveName, difficulty)) {
+ // Convert the 565 thumbnail data to the needed overlay format
+ Common::MemoryReadStream thumbStream(thumbData, 160 * 120 * 2);
+ Graphics::PixelFormat destFormat = g_system->getOverlayFormat();
+ Graphics::Surface *to = new Graphics::Surface();
+ to->create(160, 120, destFormat);
+
+ OverlayColor *pixels = (OverlayColor *)to->pixels;
+ for (int y = 0; y < to->h; ++y) {
+ for (int x = 0; x < to->w; ++x) {
+ uint8 r, g, b;
+ Graphics::colorToRGB<Graphics::ColorMasks<555> >(thumbStream.readUint16LE(), r, g, b);
+
+ // converting to current OSystem Color
+ *pixels++ = destFormat.RGBToColor(r, g, b);
+ }
+ }
+
+ // Create the return descriptor
+ SaveStateDescriptor desc(slot, saveName);
+ desc.setDeletableFlag(true);
+ desc.setWriteProtectedFlag(false);
+ desc.setThumbnail(to);
+
+ return desc;
+ }
+
+ return SaveStateDescriptor();
+}
+
+
+#if PLUGIN_ENABLED_DYNAMIC(TONY)
+REGISTER_PLUGIN_DYNAMIC(TONY, PLUGIN_TYPE_ENGINE, TonyMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(TONY, PLUGIN_TYPE_ENGINE, TonyMetaEngine);
+#endif
diff --git a/engines/tony/detection_tables.h b/engines/tony/detection_tables.h
new file mode 100644
index 0000000000..d2bd81f083
--- /dev/null
+++ b/engines/tony/detection_tables.h
@@ -0,0 +1,178 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+namespace Tony {
+
+static const TonyGameDescription gameDescriptions[] = {
+ {
+ // Tony Tough English
+ {
+ "tony",
+ 0,
+ {
+ // TODO: AdvancedDetector seems to have a problem where it thinks data1.cab is unrecognized.
+ // Is it perhaps because the Agos engine also has detection entries for data1.cab?
+ {"data1.cab", 0, "ce82907242166bfb594d97bdb68f96d2", 4350},
+ /*{"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "57c4a3860cf899443c357e0078ea6f49", 366773},*/
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // Tony Tough English Demo
+ {
+ "tony",
+ "Extracted Demo",
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 14972409},
+ {"roasted.mpc", 0, "1e247922ec869712bfd96625bc4d3c7c", 39211},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // Tony Tough English Demo (Compressed)
+ {
+ "tony",
+ "Demo",
+ {
+ {"data1.cab", 0, "7d8b6d308f96aee3968ad7910fb11e6d", 58660608},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO | GF_COMPRESSED,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough French "Collection Aventure" provided by Strangerke
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "e890c6a41238827bdfa9874a65618b69", 374135},
+ AD_LISTEND
+ },
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough German "Shoe Box" provided by Strangerke
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "ccf7ab939a34de1b13df538596431684", 389554},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough Italian provided by Fabio Barzagli
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "1dc896cdb945170d7408598f803411c1", 380001},
+ AD_LISTEND
+ },
+ Common::IT_ITA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough Polish provided by Fabio Barzagli
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "89733ea710669acc8e7900b115f4afef", 389625},
+ AD_LISTEND
+ },
+ Common::PL_POL,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough German "Gamestar" provided in bug #3566035
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "187de6f88f4083808cb66342ab55a7fd", 389904},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough Czech provided in bug #3565765
+ {
+ "tony",
+ 0,
+ {
+ // {"data1.cab", 0, "c6d5dd8f0c1241a6e3f7861b7f27bf7b", 4350},
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "a8283a101878f3ca105f1f83f07e2c40", 386491},
+ AD_LISTEND
+ },
+ Common::CZ_CZE,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ { AD_TABLE_END_MARKER }
+};
+
+} // End of namespace Tony
diff --git a/engines/tony/font.cpp b/engines/tony/font.cpp
new file mode 100644
index 0000000000..fa018b4464
--- /dev/null
+++ b/engines/tony/font.cpp
@@ -0,0 +1,1179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/textconsole.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/font.h"
+#include "tony/input.h"
+#include "tony/inventory.h"
+#include "tony/loc.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* RMFont Methods
+\****************************************************************************/
+
+RMFont::RMFont() {
+ _letter = NULL;
+ _nLetters = _fontDimx = _fontDimy = _dimx = _dimy = 0;
+}
+
+RMFont::~RMFont() {
+ unload();
+}
+
+void RMFont::load(const byte *buf, int nChars, int dimx, int dimy, uint32 palResID) {
+ _letter = new RMGfxSourceBuffer8RLEByte[nChars];
+
+ // Initialize the fonts
+ for (int i = 0; i < nChars; i++) {
+ // Initialize the buffer with the letters
+ _letter[i].init(buf + i * (dimx * dimy + 8) + 8, dimx, dimy);
+ _letter[i].loadPaletteWA(palResID);
+ }
+
+ _fontDimx = dimx;
+ _fontDimy = dimy;
+
+ _nLetters = nChars;
+}
+
+void RMFont::load(uint32 resID, int nChars, int dimx, int dimy, uint32 palResID) {
+ RMRes res(resID);
+
+ if ((int)res.size() < nChars * (dimy * dimx + 8))
+ nChars = res.size() / (dimy * dimx + 8);
+
+ load(res, nChars, dimx, dimy, palResID);
+}
+
+void RMFont::unload() {
+ if (_letter != NULL) {
+ delete[] _letter;
+ _letter = NULL;
+ }
+}
+
+
+RMGfxPrimitive *RMFont::makeLetterPrimitive(byte bChar, int &nLength) {
+ RMFontPrimitive *prim;
+
+ // Convert from character to glyph index
+ int nLett = convertToLetter(bChar);
+ assert(nLett < _nLetters);
+
+ // Create primitive font
+ prim = new RMFontPrimitive(this);
+ prim->_nChar = nLett;
+
+ // Get the length of the character in pixels
+ nLength = letterLength(bChar);
+
+ return prim;
+}
+
+void RMFont::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim2) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ RMFontPrimitive *prim = (RMFontPrimitive *)prim2;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Call the draw method of the letter assigned to the primitive
+ if (prim->_nChar != -1)
+ CORO_INVOKE_2(_letter[prim->_nChar].draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+void RMFont::close() {
+ unload();
+}
+
+int RMFont::stringLen(const Common::String &text) {
+ if (text.empty())
+ return letterLength('\0');
+
+ uint len = 0;
+ uint i;
+ for (i = 0; i < text.size() - 1; i++)
+ len += letterLength(text[i], text[i + 1]);
+ len += letterLength(text[i]);
+
+ return len;
+}
+
+int RMFont::stringLen(char bChar, char bNext) {
+ return letterLength(bChar, bNext);
+}
+
+/****************************************************************************\
+* RMFontColor Methods
+\****************************************************************************/
+
+RMFontColor::RMFontColor() : RMFont() {
+ _fontR = _fontG = _fontB = 255;
+}
+
+RMFontColor::~RMFontColor() {
+}
+
+void RMFontColor::setBaseColor(byte r1, byte g1, byte b1) {
+ int r = (int)r1 << 16;
+ int g = (int)g1 << 16;
+ int b = (int)b1 << 16;
+
+ int rstep = r / 14;
+ int gstep = g / 14;
+ int bstep = b / 14;
+
+ byte pal[768 * 3];
+
+ // Check if we are already on the right color
+ if (_fontR == r1 && _fontG == g1 && _fontB == b1)
+ return;
+
+ _fontR = r1;
+ _fontG = g1;
+ _fontB = b1;
+
+ // Constructs a new palette for the font
+ for (int i = 1; i < 16; i++) {
+ pal[i * 3 + 0] = r >> 16;
+ pal[i * 3 + 1] = g >> 16;
+ pal[i * 3 + 2] = b >> 16;
+
+ r -= rstep;
+ g -= gstep;
+ b -= bstep;
+ }
+
+ pal[15 * 3 + 0] += 8;
+ pal[15 * 3 + 1] += 8;
+ pal[15 * 3 + 2] += 8;
+
+ // Puts in all the letters
+ for (int i = 0; i < _nLetters; i++)
+ _letter[i].loadPaletteWA(pal);
+}
+
+/***************************************************************************\
+* RMFontWithTables Methods
+\****************************************************************************/
+int RMFontWithTables::convertToLetter(byte nChar) {
+ return _cTable[nChar];
+}
+
+int RMFontWithTables::letterLength(int nChar, int nNext) {
+ return (nChar != -1 ? _lTable[(byte)nChar] + _l2Table[(byte)nChar][(byte)nNext] : _lDefault);
+}
+
+/***************************************************************************\
+* RMFontDialog Methods
+\****************************************************************************/
+
+void RMFontDialog::init() {
+ // bernie: Number of characters in the font
+ int nchars =
+ 112 // base
+ + 18 // polish
+ + 66 // russian
+ + 30 // czech
+ + 8 // french
+ + 5; // deutsch
+
+ load(RES_F_PARL, nchars, 20, 20);
+
+ // Initialize the font table
+ _lDefault = 13;
+ _hDefault = 18;
+ Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
+
+ for (int i = 0; i < 256; i++) {
+ _cTable[i] = g_vm->_cTableDialog[i];
+ _lTable[i] = g_vm->_lTableDialog[i];
+ }
+}
+
+
+/***************************************************************************\
+* RMFontMacc Methods
+\****************************************************************************/
+
+void RMFontMacc::init() {
+ // bernie: Number of characters in the font
+ int nchars =
+ 102 // base
+ + 18 // polish
+ + 66 // russian
+ + 30 // czech
+ + 8 // francais
+ + 5; // deutsch
+
+ load(RES_F_MACC, nchars, 11, 16);
+
+ // Default
+ _lDefault = 10;
+ _hDefault = 17;
+ Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
+
+ for (int i = 0; i < 256; i++) {
+ _cTable[i] = g_vm->_cTableMacc[i];
+ _lTable[i] = g_vm->_lTableMacc[i];
+ }
+}
+
+/***************************************************************************\
+* RMFontCredits Methods
+\****************************************************************************/
+
+void RMFontCredits::init() {
+ // bernie: Number of characters in the font
+ int nchars =
+ 112 // base
+ + 18 // polish
+ + 66 // russian
+ + 30 // czech
+ + 8 // french
+ + 2; // deutsch
+
+ load(RES_F_CREDITS, nchars, 27, 28, RES_F_CPAL);
+
+ // Default
+ _lDefault = 10;
+ _hDefault = 28;
+ Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
+
+ for (int i = 0; i < 256; i++) {
+ _cTable[i] = g_vm->_cTableCred[i];
+ _lTable[i] = g_vm->_lTableCred[i];
+ }
+}
+
+
+
+/***************************************************************************\
+* RMFontObj Methods
+\****************************************************************************/
+
+#define TOUPPER(a) ((a) >= 'a' && (a) <= 'z' ? (a) + 'A' - 'a' : (a))
+#define TOLOWER(a) ((a) >= 'A' && (a) <= 'Z' ? (a) + 'a' - 'A' : (a))
+
+void RMFontObj::setBothCase(int nChar, int nNext, signed char spiazz) {
+ _l2Table[TOUPPER(nChar)][TOUPPER(nNext)] = spiazz;
+ _l2Table[TOUPPER(nChar)][TOLOWER(nNext)] = spiazz;
+ _l2Table[TOLOWER(nChar)][TOUPPER(nNext)] = spiazz;
+ _l2Table[TOLOWER(nChar)][TOLOWER(nNext)] = spiazz;
+}
+
+void RMFontObj::init() {
+ //bernie: Number of characters in the font (solo maiuscolo)
+ int nchars =
+ 85 // base
+ + 9 // polish
+ + 33 // russian
+ + 15 // czech
+ + 0 // francais (no uppercase chars)
+ + 1; // deutsch
+
+ load(RES_F_OBJ, nchars, 25, 30);
+
+ // Initialize the font table
+ _lDefault = 26;
+ _hDefault = 30;
+ Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
+
+ for (int i = 0; i < 256; i++) {
+ _cTable[i] = g_vm->_cTableObj[i];
+ _lTable[i] = g_vm->_lTableObj[i];
+ }
+
+ // Special case
+ setBothCase('C', 'C', 2);
+ setBothCase('A', 'T', -2);
+ setBothCase('R', 'S', 2);
+ setBothCase('H', 'I', -2);
+ setBothCase('T', 'S', 2);
+ setBothCase('O', 'R', 2);
+ setBothCase('O', 'L', 2);
+ setBothCase('O', 'G', 2);
+ setBothCase('Z', 'A', -1);
+ setBothCase('R', 'R', 1);
+ setBothCase('R', 'U', 3);
+}
+
+/****************************************************************************\
+* RMText Methods
+\****************************************************************************/
+
+RMFontColor *RMText::_fonts[4] = { NULL, NULL, NULL, NULL };
+
+void RMText::initStatics() {
+ Common::fill(&_fonts[0], &_fonts[4], (RMFontColor *)NULL);
+}
+
+RMText::RMText() {
+ // Default color: white
+ _textR = _textG = _textB = 255;
+
+ // Default length
+ _maxLineLength = 350;
+
+ _bTrasp0 = true;
+ _aHorType = HCENTER;
+ _aVerType = VTOP;
+ setPriority(150);
+}
+
+RMText::~RMText() {
+}
+
+void RMText::unload() {
+ if (_fonts[0] != NULL) {
+ delete _fonts[0];
+ delete _fonts[1];
+ delete _fonts[2];
+ delete _fonts[3];
+ _fonts[0] = _fonts[1] = _fonts[2] = _fonts[3] = 0;
+ }
+}
+
+void RMText::setMaxLineLength(int max) {
+ _maxLineLength = max;
+}
+
+void RMText::removeThis(CORO_PARAM, bool &result) {
+ // Here we can do checks on the number of frames, time spent, etc.
+ result = true;
+}
+
+void RMText::writeText(const Common::String &text, int nFont, int *time) {
+ // Initializes the font (only once)
+ if (_fonts[0] == NULL) {
+ _fonts[0] = new RMFontDialog;
+ _fonts[0]->init();
+ _fonts[1] = new RMFontObj;
+ _fonts[1]->init();
+ _fonts[2] = new RMFontMacc;
+ _fonts[2]->init();
+ _fonts[3] = new RMFontCredits;
+ _fonts[3]->init();
+ }
+
+ writeText(text, _fonts[nFont], time);
+}
+
+void RMText::writeText(Common::String text, RMFontColor *font, int *time) {
+ RMGfxPrimitive *prim;
+
+ // Set the base color
+ font->setBaseColor(_textR, _textG, _textB);
+
+ // Destroy the buffer before starting
+ destroy();
+
+ // If the string is empty, do nothing
+ if (text.empty())
+ return;
+
+ // Divide the words into lines. In this cycle, X contains the maximum length reached by a line,
+ // and the number of lines
+ Common::Array<Common::String> lines;
+ uint p = 0;
+ int j = 0;
+ int x = 0;
+ while (p < text.size()) {
+ j += font->stringLen(text[p]);
+ if (j > (((_aHorType == HLEFTPAR) && (lines.size() > 0)) ? _maxLineLength - 25 : _maxLineLength)) {
+ j -= font->stringLen(text[p], (p + 1 == text.size()) ? '\0' : text[p + 1]);
+ if (j > x)
+ x = j;
+
+ // Back to the first usable space
+ //
+ // BERNIE: In the original, sentences containing words that exceed the
+ // width of a line caused discontinuation of the whole sentence.
+ // This workaround has the partial word broken up so it will still display
+ //
+ uint old_p = p;
+ while (text[p] != ' ' && text[p] != '-' && p > 0)
+ p--;
+
+ if (p == 0)
+ p = old_p;
+
+ // Check if there are any blanks to end
+ while ((text[p] == ' ' || text[p] == '-') && p + 1 < text.size())
+ p++;
+ if (p == text.size())
+ break;
+ lines.push_back(Common::String(text.c_str(), p));
+ if (text[p] == ' ')
+ p++;
+ text = text.c_str() + p;
+ p = 0;
+ j = 0;
+ continue;
+ }
+ p++;
+ }
+
+ if (j > x)
+ x = j;
+
+ // Add the last line of text.
+ lines.push_back(text);
+
+ x += 8;
+
+ // Starting position for the surface: X1, Y
+ int width = x;
+ int height = (lines.size() - 1) * font->letterHeight() + font->_fontDimy;
+
+ // Create the surface
+ create(width, height);
+ Common::fill(_buf, _buf + width * height * 2, 0);
+
+ p = 0;
+
+ int y = 0;
+ int numchar = 0;
+ for (uint i = 0; i < lines.size(); ++i) {
+ const Common::String &line = lines[i];
+
+ // Measure the length of the line
+ x = 0;
+ j = font->stringLen(line);
+
+ switch (_aHorType) {
+ case HLEFT:
+ x = 0;
+ break;
+
+ case HLEFTPAR:
+ if (i == 0)
+ x = 0;
+ else
+ x = 25;
+ break;
+
+ case HCENTER:
+ x = width / 2 - j / 2;
+ break;
+
+ case HRIGHT:
+ x = width - j - 1;
+ break;
+ }
+
+ p = 0;
+ while (p < line.size()) {
+ if (line[p] == ' ') {
+ x += font->stringLen(line[p]);
+ p++;
+ continue;
+ }
+
+ int len;
+ prim = font->makeLetterPrimitive(line[p], len);
+ prim->getDst()._x1 = x;
+ prim->getDst()._y1 = y;
+ addPrim(prim);
+
+ numchar++;
+
+ x += font->stringLen(line[p], (p + 1 == line.size()) ? '\0' : line[p + 1]);
+ p++;
+ }
+ p++;
+ y += font->letterHeight();
+ }
+
+ if (time != NULL)
+ *time = 1000 + numchar * (11 - GLOBALS._nCfgTextSpeed) * 14;
+}
+
+void RMText::clipOnScreen(RMGfxPrimitive *prim) {
+ // Don't let it go outside the screen
+ if (prim->getDst()._x1 < 5)
+ prim->getDst()._x1 = 5;
+ if (prim->getDst()._y1 < 5)
+ prim->getDst()._y1 = 5;
+ if (prim->getDst()._x1 + _dimx > 635)
+ prim->getDst()._x1 = 635 - _dimx;
+ if (prim->getDst()._y1 + _dimy > 475)
+ prim->getDst()._y1 = 475 - _dimy;
+}
+
+void RMText::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+ // Horizontally
+ if (_aHorType == HCENTER)
+ prim->getDst().topLeft() -= RMPoint(_dimx / 2, 0);
+ else if (_aHorType == HRIGHT)
+ prim->getDst().topLeft() -= RMPoint(_dimx, 0);
+
+
+ // Vertically
+ if (_aVerType == VTOP) {
+
+ } else if (_aVerType == VCENTER) {
+ prim->getDst()._y1 -= _dimy / 2;
+
+ } else if (_aVerType == VBOTTOM) {
+ prim->getDst()._y1 -= _dimy;
+ }
+
+ clipOnScreen(prim);
+
+ CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+/**
+ * Set the alignment type
+ */
+void RMText::setAlignType(HorAlign aHor, VerAlign aVer) {
+ _aHorType = aHor;
+ _aVerType = aVer;
+}
+
+/**
+ * Set the base color
+ */
+void RMText::setColor(byte r, byte g, byte b) {
+ _textR = r;
+ _textG = g;
+ _textB = b;
+}
+
+/****************************************************************************\
+* RMTextDialog Methods
+\****************************************************************************/
+
+RMTextDialog::RMTextDialog() : RMText() {
+ _time = _startTime = 0;
+ _dst = RMPoint(0, 0);
+
+ _bSkipStatus = true;
+ _bShowed = true;
+ _bForceTime = false;
+ _bForceNoTime = false;
+ _bAlwaysDisplay = false;
+ _bNoTab = false;
+ _hCustomSkip = CORO_INVALID_PID_VALUE;
+ _hCustomSkip2 = CORO_INVALID_PID_VALUE;
+ _input = NULL;
+
+ // Create the event for displaying the end
+ _hEndDisplay = CoroScheduler.createEvent(false, false);
+}
+
+RMTextDialog::~RMTextDialog() {
+ CoroScheduler.closeEvent(_hEndDisplay);
+}
+
+void RMTextDialog::show() {
+ _bShowed = true;
+}
+
+void RMTextDialog::hide(CORO_PARAM) {
+ _bShowed = false;
+}
+
+void RMTextDialog::writeText(const Common::String &text, int font, int *time) {
+ RMText::writeText(text, font, &_time);
+
+ if (time != NULL)
+ *time = _time;
+}
+
+void RMTextDialog::writeText(const Common::String &text, RMFontColor *font, int *time) {
+ RMText::writeText(text, font, &_time);
+
+ if (time != NULL)
+ *time = _time;
+}
+
+
+void RMTextDialog::setSkipStatus(bool bEnabled) {
+ _bSkipStatus = bEnabled;
+}
+
+void RMTextDialog::forceTime() {
+ _bForceTime = true;
+}
+
+void RMTextDialog::forceNoTime() {
+ _bForceNoTime = true;
+}
+
+void RMTextDialog::setNoTab() {
+ _bNoTab = true;
+}
+
+void RMTextDialog::setForcedTime(uint32 dwTime) {
+ _time = dwTime;
+}
+
+void RMTextDialog::setAlwaysDisplay() {
+ _bAlwaysDisplay = true;
+}
+
+void RMTextDialog::removeThis(CORO_PARAM, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ bool expired;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Presume successful result
+ result = true;
+
+ // Don't erase the background
+ if (_bSkipStatus) {
+ if (!(GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE)) {
+ if (GLOBALS._bCfgTimerizedText) {
+ if (!_bForceNoTime) {
+ if (g_vm->getTime() > (uint32)_time + _startTime)
+ return;
+ }
+ }
+ }
+
+ if (!_bNoTab) {
+ if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_TAB))
+ return;
+ }
+
+ if (!_bNoTab) {
+ if (_input) {
+ if (_input->mouseLeftClicked() || _input->mouseRightClicked())
+ return;
+ }
+ }
+ }
+ // Erase the background
+ else if (!(GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE)) {
+ if (!_bForceNoTime) {
+ if (g_vm->getTime() > (uint32)_time + _startTime)
+ return;
+ }
+ }
+
+ // If time is forced
+ if (_bForceTime) {
+ if (g_vm->getTime() > (uint32)_time + _startTime)
+ return;
+ }
+
+ if (_hCustomSkip != CORO_INVALID_PID_VALUE) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _hCustomSkip, 0, &_ctx->expired);
+ // == WAIT_OBJECT_0
+ if (!_ctx->expired)
+ return;
+ }
+
+ if (GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _hCustomSkip2, 0, &_ctx->expired);
+ // == WAIT_OBJECT_0
+ if (!_ctx->expired)
+ return;
+ }
+
+ result = false;
+
+ CORO_END_CODE;
+}
+
+void RMTextDialog::unregister() {
+ RMGfxTask::unregister();
+ assert(_nInList == 0);
+ CoroScheduler.setEvent(_hEndDisplay);
+}
+
+void RMTextDialog::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_startTime == 0)
+ _startTime = g_vm->getTime();
+
+ if (_bShowed) {
+ if (GLOBALS._bShowSubtitles || _bAlwaysDisplay) {
+ prim->getDst().topLeft() = _dst;
+ CORO_INVOKE_2(RMText::draw, bigBuf, prim);
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMTextDialog::setCustomSkipHandle(uint32 hCustom) {
+ _hCustomSkip = hCustom;
+}
+
+void RMTextDialog::setCustomSkipHandle2(uint32 hCustom) {
+ _hCustomSkip2 = hCustom;
+}
+
+void RMTextDialog::waitForEndDisplay(CORO_PARAM) {
+ CoroScheduler.waitForSingleObject(coroParam, _hEndDisplay, CORO_INFINITE);
+}
+
+void RMTextDialog::setInput(RMInput *input) {
+ _input = input;
+}
+
+/**
+ * Set the position
+ */
+void RMTextDialog::setPosition(const RMPoint &pt) {
+ _dst = pt;
+}
+
+/****************************************************************************\
+* RMTextDialogScrolling Methods
+\****************************************************************************/
+
+RMTextDialogScrolling::RMTextDialogScrolling() {
+ _curLoc = NULL;
+}
+
+RMTextDialogScrolling::RMTextDialogScrolling(RMLocation *loc) {
+ _curLoc = loc;
+ _startScroll = loc->scrollPosition();
+}
+
+RMTextDialogScrolling::~RMTextDialogScrolling() {
+}
+
+void RMTextDialogScrolling::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ RMPoint curDst;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->curDst = _dst;
+
+ if (_curLoc != NULL)
+ _dst -= _curLoc->scrollPosition() - _startScroll;
+
+ CORO_INVOKE_2(RMTextDialog::draw, bigBuf, prim);
+
+ _dst = _ctx->curDst;
+
+ CORO_END_CODE;
+}
+
+void RMTextDialogScrolling::clipOnScreen(RMGfxPrimitive *prim) {
+ // We must not do anything!
+}
+
+
+/****************************************************************************\
+* RMTextItemName Methods
+\****************************************************************************/
+
+RMTextItemName::RMTextItemName() : RMText() {
+ _item = NULL;
+ setPriority(220);
+}
+
+RMTextItemName::~RMTextItemName() {
+}
+
+void RMTextItemName::doFrame(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMLocation &loc, RMPointer &ptr, RMInventory &inv) {
+ CORO_BEGIN_CONTEXT;
+ RMItem *lastItem;
+ uint32 hThread;
+ CORO_END_CONTEXT(_ctx);
+
+ Common::String itemName;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->lastItem = _item;
+
+ // Adds to the list if there is need
+ if (!_nInList)
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ // Update the scrolling co-ordinates
+ _curscroll = loc.scrollPosition();
+
+ // Check if we are on the inventory
+ if (inv.itemInFocus(_mpos))
+ _item = inv.whichItemIsIn(_mpos);
+ else
+ _item = loc.whichItemIsIn(_mpos);
+
+ // If there an item, get its name
+ if (_item != NULL)
+ _item->getName(itemName);
+
+ // Write it
+ writeText(itemName, 1);
+
+ // Handle the change If the selected item is different from the previous one
+ if (_ctx->lastItem != _item) {
+ if (_item == NULL)
+ ptr.setSpecialPointer(RMPointer::PTR_NONE);
+ else {
+ _ctx->hThread = mpalQueryDoAction(20, _item->mpalCode(), 0);
+ if (_ctx->hThread == CORO_INVALID_PID_VALUE)
+ ptr.setSpecialPointer(RMPointer::PTR_NONE);
+ else
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->hThread, CORO_INFINITE);
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+
+void RMTextItemName::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // If there is no text, it's pointless to continue
+ if (_buf == NULL)
+ return;
+
+ // Set the destination coordinates of the mouse
+ prim->getDst().topLeft() = _mpos - RMPoint(0, 30);
+
+ CORO_INVOKE_2(RMText::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+RMPoint RMTextItemName::getHotspot() {
+ if (_item == NULL)
+ return _mpos + _curscroll;
+ else
+ return _item->getHotspot();
+}
+
+RMItem *RMTextItemName::getSelectedItem() {
+ return _item;
+}
+
+bool RMTextItemName::isItemSelected() {
+ return _item != NULL;
+}
+
+void RMTextItemName::setMouseCoord(const RMPoint &m) {
+ _mpos = m;
+}
+
+void RMTextItemName::removeThis(CORO_PARAM, bool &result) {
+ result = true;
+}
+
+/****************************************************************************\
+* RMDialogChoice Methods
+\****************************************************************************/
+
+RMDialogChoice::RMDialogChoice() {
+ RMResRaw dlg1(RES_I_DLGTEXT);
+ RMResRaw dlg2(RES_I_DLGTEXTLINE);
+ RMRes dlgpal(RES_I_DLGTEXTPAL);
+
+ _dlgText.init(dlg1, dlg1.width(), dlg1.height());
+ _dlgTextLine.init(dlg2, dlg2.width(), dlg2.height());
+
+ _dlgText.loadPaletteWA(dlgpal);
+ _dlgTextLine.loadPaletteWA(dlgpal);
+
+ _hUnreg = CoroScheduler.createEvent(false, false);
+ _bRemoveFromOT = false;
+
+ _curAdded = 0;
+ _bShow = false;
+}
+
+RMDialogChoice::~RMDialogChoice() {
+ CoroScheduler.closeEvent(_hUnreg);
+}
+
+void RMDialogChoice::unregister() {
+ RMGfxWoodyBuffer::unregister();
+ assert(!_nInList);
+ CoroScheduler.pulseEvent(_hUnreg);
+
+ _bRemoveFromOT = false;
+}
+
+void RMDialogChoice::init() {
+ _numChoices = 0;
+ _drawedStrings = NULL;
+ _ptDrawStrings = NULL;
+ _curSelection = -1;
+
+ create(640, 477);
+ setPriority(140);
+}
+
+
+void RMDialogChoice::close() {
+ if (_drawedStrings != NULL) {
+ delete[] _drawedStrings;
+ _drawedStrings = NULL;
+ }
+
+ if (_ptDrawStrings != NULL) {
+ delete[] _ptDrawStrings;
+ _ptDrawStrings = NULL;
+ }
+
+ destroy();
+}
+
+void RMDialogChoice::setNumChoices(int num) {
+ _numChoices = num;
+ _curAdded = 0;
+
+ // Allocate space for drawn strings
+ _drawedStrings = new RMText[num];
+ _ptDrawStrings = new RMPoint[num];
+
+ // Initialization
+ for (int i = 0; i < _numChoices; i++) {
+ _drawedStrings[i].setColor(0, 255, 0);
+ _drawedStrings[i].setAlignType(RMText::HLEFTPAR, RMText::VTOP);
+ _drawedStrings[i].setMaxLineLength(600);
+ _drawedStrings[i].setPriority(10);
+ }
+}
+
+void RMDialogChoice::addChoice(const Common::String &string) {
+ // Draw the string
+ assert(_curAdded < _numChoices);
+ _drawedStrings[_curAdded++].writeText(string, 0);
+}
+
+void RMDialogChoice::prepare(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ RMPoint ptPos;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ addPrim(new RMGfxPrimitive(&_dlgText, RMPoint(0, 0)));
+ addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155)));
+ addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83)));
+ addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83)));
+ addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83 + 83)));
+
+ _ctx->ptPos.set(20, 90);
+
+ for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) {
+ addPrim(new RMGfxPrimitive(&_drawedStrings[_ctx->i], _ctx->ptPos));
+ _ptDrawStrings[_ctx->i] = _ctx->ptPos;
+ _ctx->ptPos.offset(0, _drawedStrings[_ctx->i].getDimy() + 15);
+ }
+
+ CORO_INVOKE_0(drawOT);
+ clearOT();
+
+ _ptDrawPos.set(0, 480 - _ctx->ptPos._y);
+
+ CORO_END_CODE;
+}
+
+void RMDialogChoice::setSelected(CORO_PARAM, int pos) {
+ CORO_BEGIN_CONTEXT;
+ RMGfxBox box;
+ RMRect rc;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (pos == _curSelection)
+ return;
+
+ _ctx->box.setPriority(5);
+
+ if (_curSelection != -1) {
+ _ctx->box.setColor(0xCC, 0xCC, 0xFF);
+ _ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[_curSelection]._y);
+ _ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[_curSelection].getDimy());
+ addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc));
+
+ addPrim(new RMGfxPrimitive(&_drawedStrings[_curSelection], _ptDrawStrings[_curSelection]));
+ CORO_INVOKE_0(drawOT);
+ clearOT();
+ }
+
+ if (pos != -1) {
+ _ctx->box.setColor(100, 100, 100);
+ _ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[pos]._y);
+ _ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[pos].getDimy());
+ addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc));
+ addPrim(new RMGfxPrimitive(&_drawedStrings[pos], _ptDrawStrings[pos]));
+ }
+
+ CORO_INVOKE_0(drawOT);
+ clearOT();
+
+ _curSelection = pos;
+
+ CORO_END_CODE;
+}
+
+void RMDialogChoice::show(CORO_PARAM, RMGfxTargetBuffer *bigBuf) {
+ CORO_BEGIN_CONTEXT;
+ RMPoint destpt;
+ int deltay;
+ int starttime;
+ int elaps;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_0(prepare);
+ _bShow = false;
+
+ if (!_nInList && bigBuf != NULL)
+ bigBuf->addPrim(new RMGfxPrimitive(this));
+
+ if (0) {
+ _bShow = true;
+ } else {
+ _ctx->starttime = g_vm->getTime();
+ _ctx->deltay = 480 - _ptDrawPos._y;
+ _ctx->destpt = _ptDrawPos;
+ _ptDrawPos.set(0, 480);
+
+ if (!_nInList && bigBuf != NULL)
+ bigBuf->addPrim(new RMGfxPrimitive(this));
+ _bShow = true;
+
+ _ctx->elaps = 0;
+ while (_ctx->elaps < 700) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ _ctx->elaps = g_vm->getTime() - _ctx->starttime;
+ _ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * _ctx->elaps) / 100;
+ }
+
+ _ptDrawPos._y = _ctx->destpt._y;
+ }
+
+ CORO_END_CODE;
+}
+
+void RMDialogChoice::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bShow == false)
+ return;
+
+ prim->setDst(_ptDrawPos);
+ CORO_INVOKE_2(RMGfxSourceBuffer16::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+
+void RMDialogChoice::hide(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ int deltay;
+ int starttime;
+ int elaps;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (1) {
+ _ctx->starttime = g_vm->getTime();
+
+ _ctx->deltay = 480 - _ptDrawPos._y;
+ _ctx->elaps = 0;
+ while (_ctx->elaps < 700) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ _ctx->elaps = g_vm->getTime() - _ctx->starttime;
+ _ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * (700 - _ctx->elaps)) / 100;
+ }
+ }
+
+ _bShow = false;
+ _bRemoveFromOT = true;
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hUnreg, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+
+void RMDialogChoice::removeThis(CORO_PARAM, bool &result) {
+ result = _bRemoveFromOT;
+}
+
+void RMDialogChoice::doFrame(CORO_PARAM, RMPoint ptMousePos) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (ptMousePos._y > _ptDrawPos._y) {
+ for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) {
+ if ((ptMousePos._y >= _ptDrawPos._y + _ptDrawStrings[_ctx->i]._y) && (ptMousePos._y < _ptDrawPos._y + _ptDrawStrings[_ctx->i]._y + _drawedStrings[_ctx->i].getDimy())) {
+ CORO_INVOKE_1(setSelected, _ctx->i);
+ break;
+ }
+ }
+
+ if (_ctx->i == _numChoices)
+ CORO_INVOKE_1(setSelected, -1);
+ }
+
+ CORO_END_CODE;
+}
+
+int RMDialogChoice::getSelection() {
+ return _curSelection;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/font.h b/engines/tony/font.h
new file mode 100644
index 0000000000..13c1ddf268
--- /dev/null
+++ b/engines/tony/font.h
@@ -0,0 +1,379 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_FONT_H
+#define TONY_FONT_H
+
+#include "common/system.h"
+#include "common/coroutines.h"
+#include "tony/gfxcore.h"
+#include "tony/resid.h"
+
+namespace Tony {
+
+class RMInput;
+class RMInventory;
+class RMItem;
+class RMLoc;
+class RMLocation;
+class RMPointer;
+
+/**
+ * Manages a font, in which there is a different surface for each letter
+ */
+class RMFont : public RMGfxTaskSetPrior {
+protected:
+ int _nLetters;
+ RMGfxSourceBuffer8RLEByte *_letter;
+public:
+ int _fontDimx, _fontDimy;
+
+private:
+ int _dimx, _dimy;
+
+ class RMFontPrimitive : public RMGfxPrimitive {
+ public:
+ RMFontPrimitive() : RMGfxPrimitive() { _nChar = 0; }
+ RMFontPrimitive(RMGfxTask *task) : RMGfxPrimitive(task) { _nChar = 0; }
+ virtual ~RMFontPrimitive() { }
+ virtual RMGfxPrimitive *duplicate() {
+ return new RMFontPrimitive(*this);
+ }
+
+ int _nChar;
+ };
+
+protected:
+ // Loads the font
+ void load(uint32 resID, int nChars, int dimx, int dimy, uint32 palResID = RES_F_PAL);
+ void load(const byte *buf, int nChars, int dimx, int dimy, uint32 palResID = RES_F_PAL);
+
+ // Remove the font
+ void unload();
+
+protected:
+ // Conversion form character to font index
+ virtual int convertToLetter(byte nChar) = 0;
+
+ // Character width
+ virtual int letterLength(int nChar, int nNext = 0) = 0;
+
+public:
+ virtual int letterHeight() = 0;
+
+public:
+ RMFont();
+ virtual ~RMFont();
+
+ // Initialization and closing
+ virtual void init() = 0;
+ virtual void close();
+
+ // Drawing
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBug, RMGfxPrimitive *prim);
+
+ // Create a primitive for a letter
+ RMGfxPrimitive *makeLetterPrimitive(byte bChar, int &nLength);
+
+ // Length in pixels of a string with the current font
+ int stringLen(const Common::String &text);
+ int stringLen(char bChar, char bNext = 0);
+};
+
+
+class RMFontColor : public virtual RMFont {
+private:
+ byte _fontR, _fontG, _fontB;
+
+public:
+ RMFontColor();
+ virtual ~RMFontColor();
+ virtual void setBaseColor(byte r, byte g, byte b);
+};
+
+
+class RMFontWithTables : public virtual RMFont {
+protected:
+ int _cTable[256];
+ int _lTable[256];
+ int _lDefault;
+ int _hDefault;
+ signed char _l2Table[256][256];
+
+protected:
+ // Overloaded methods
+ int convertToLetter(byte nChar);
+ int letterLength(int nChar, int nNext = 0);
+
+public:
+ int letterHeight() {
+ return _hDefault;
+ }
+ virtual ~RMFontWithTables() {}
+};
+
+
+class RMFontDialog : public RMFontColor, public RMFontWithTables {
+public:
+ void init();
+ virtual ~RMFontDialog() {}
+};
+
+class RMFontObj : public RMFontColor, public RMFontWithTables {
+private:
+ void setBothCase(int nChar, int nNext, signed char spiazz);
+
+public:
+ void init();
+ virtual ~RMFontObj() {}
+};
+
+class RMFontMacc : public RMFontColor, public RMFontWithTables {
+public:
+ void init();
+ virtual ~RMFontMacc() {}
+};
+
+class RMFontCredits : public RMFontColor, public RMFontWithTables {
+public:
+ void init();
+ virtual ~RMFontCredits() {}
+ virtual void setBaseColor(byte r, byte g, byte b) {}
+};
+
+/**
+ * Manages writing text onto9 the screen
+ */
+class RMText : public RMGfxWoodyBuffer {
+private:
+ static RMFontColor *_fonts[4];
+ int _maxLineLength;
+
+public:
+ enum HorAlign {
+ HLEFT,
+ HLEFTPAR,
+ HCENTER,
+ HRIGHT
+ };
+
+ enum VerAlign {
+ VTOP,
+ VCENTER,
+ VBOTTOM
+ };
+
+private:
+ HorAlign _aHorType;
+ VerAlign _aVerType;
+ byte _textR, _textG, _textB;
+
+protected:
+ virtual void clipOnScreen(RMGfxPrimitive *prim);
+
+public:
+ RMText();
+ virtual ~RMText();
+ static void initStatics();
+ static void unload();
+
+ // Set the alignment type
+ void setAlignType(HorAlign aHor, VerAlign aVer);
+
+ // Sets the maximum length of a line in pixels (used to format the text)
+ void setMaxLineLength(int max);
+
+ // Write the text
+ void writeText(const Common::String &text, int font, int *time = NULL);
+ void writeText(Common::String text, RMFontColor *font, int *time = NULL);
+
+ // Overloaded function to decide when you delete the object from the OT list
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Overloading of the Draw to center the text, if necessary
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Set the base color
+ void setColor(byte r, byte g, byte b);
+};
+
+/**
+ * Manages text in a dialog
+ */
+class RMTextDialog : public RMText {
+protected:
+ int _startTime;
+ int _time;
+ bool _bSkipStatus;
+ RMPoint _dst;
+ uint32 _hEndDisplay;
+ bool _bShowed;
+ bool _bForceTime;
+ bool _bForceNoTime;
+ uint32 _hCustomSkip;
+ uint32 _hCustomSkip2;
+ RMInput *_input;
+ bool _bAlwaysDisplay;
+ bool _bNoTab;
+
+public:
+ RMTextDialog();
+ virtual ~RMTextDialog();
+
+ // Write the text
+ void writeText(const Common::String &text, int font, int *time = NULL);
+ void writeText(const Common::String &text, RMFontColor *font, int *time = NULL);
+
+ // Overloaded function to decide when you delete the object from the OT list
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Overloaded de-registration
+ virtual void unregister();
+
+ // Overloading of the Draw to center the text, if necessary
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Set the position
+ void setPosition(const RMPoint &pt);
+
+ // Waiting
+ void waitForEndDisplay(CORO_PARAM);
+ void setCustomSkipHandle(uint32 hCustomSkip);
+ void setCustomSkipHandle2(uint32 hCustomSkip);
+ void setSkipStatus(bool bEnabled);
+ void setForcedTime(uint32 dwTime);
+ void setNoTab();
+ void forceTime();
+ void forceNoTime();
+ void setAlwaysDisplay();
+
+ // Set the input device, to allow skip from mouse
+ void setInput(RMInput *input);
+
+ void show();
+ void hide(CORO_PARAM);
+};
+
+class RMTextDialogScrolling : public RMTextDialog {
+protected:
+ RMLocation *_curLoc;
+ RMPoint _startScroll;
+
+ virtual void clipOnScreen(RMGfxPrimitive *prim);
+
+public:
+ RMTextDialogScrolling();
+ RMTextDialogScrolling(RMLocation *loc);
+ virtual ~RMTextDialogScrolling();
+
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Manages the name of a selected item on the screen
+ */
+class RMTextItemName : protected RMText {
+protected:
+ RMPoint _mpos;
+ RMPoint _curscroll;
+ RMItem *_item;
+
+public:
+ RMTextItemName();
+ virtual ~RMTextItemName();
+
+ void setMouseCoord(const RMPoint &m);
+
+ void doFrame(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMLocation &loc, RMPointer &ptr, RMInventory &inv);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ RMPoint getHotspot();
+ RMItem *getSelectedItem();
+ bool isItemSelected();
+
+ virtual void removeThis(CORO_PARAM, bool &result);
+};
+
+
+/**
+ * Manages the selection of screen items in a box
+ */
+class RMDialogChoice : public RMGfxWoodyBuffer {
+private:
+ int _curSelection;
+ int _numChoices;
+ RMText *_drawedStrings;
+ RMPoint *_ptDrawStrings;
+ int _curAdded;
+ bool _bShow;
+ RMGfxSourceBuffer8 _dlgText;
+ RMGfxSourceBuffer8 _dlgTextLine;
+ RMPoint _ptDrawPos;
+ uint32 _hUnreg;
+ bool _bRemoveFromOT;
+
+protected:
+ void prepare(CORO_PARAM);
+ void setSelected(CORO_PARAM, int pos);
+
+public:
+ virtual void removeThis(CORO_PARAM, bool &result);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ void unregister();
+
+public:
+ // Initialization
+ RMDialogChoice();
+ virtual ~RMDialogChoice();
+
+ // Initialization and closure
+ void init();
+ void close();
+
+ // Sets the number of possible sentences, which then be added with AddChoice()
+ void setNumChoices(int num);
+
+ // Adds a string with the choice
+ void addChoice(const Common::String &string);
+
+ // Show and hide the selection, with possible animations.
+ // NOTE: If no parameter is passed to Show(), it is the obligation of
+ // caller to ensure that the class is inserted into OT list
+ void show(CORO_PARAM, RMGfxTargetBuffer *bigBuf = NULL);
+ void hide(CORO_PARAM);
+
+ // Polling Update
+ void doFrame(CORO_PARAM, RMPoint ptMousePos);
+
+ // Returns the currently selected item, or -1 if none is selected
+ int getSelection();
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/game.cpp b/engines/tony/game.cpp
new file mode 100644
index 0000000000..1a19f2836c
--- /dev/null
+++ b/engines/tony/game.cpp
@@ -0,0 +1,1604 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/textconsole.h"
+#include "graphics/cursorman.h"
+#include "tony/mpal/lzo.h"
+#include "tony/mpal/memory.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/game.h"
+#include "tony/gfxengine.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+using namespace MPAL;
+
+// Global functions
+void mainEnableGUI() {
+ g_vm->getEngine()->_bGUIInterface = true;
+ g_vm->getEngine()->_bGUIInventory = true;
+ g_vm->getEngine()->_bGUIOption = true;
+}
+
+void mainDisableGUI() {
+ g_vm->getEngine()->_bGUIInterface = false;
+ g_vm->getEngine()->_bGUIInventory = false;
+ g_vm->getEngine()->_bGUIOption = false;
+}
+
+/****************************************************************************\
+* RMOptionButton Methods
+\****************************************************************************/
+
+RMOptionButton::RMOptionButton(uint32 dwRes, RMPoint pt, bool bDoubleState) {
+ RMResRaw raw(dwRes);
+ assert(raw.isValid());
+ _buf = new RMGfxSourceBuffer16(false);
+ _buf->init(raw, raw.width(), raw.height());
+
+ _rect.setRect(pt._x, pt._y, pt._x + raw.width() - 1, pt._y + raw.height() - 1);
+ _bActive = false;
+ _bHasGfx = true;
+ _bDoubleState = bDoubleState;
+}
+
+RMOptionButton::RMOptionButton(const RMRect &pt) {
+ _rect = pt;
+ _bActive = false;
+ _bHasGfx = false;
+ _bDoubleState = false;
+ _buf = NULL;
+}
+
+RMOptionButton::~RMOptionButton() {
+ if (_bHasGfx)
+ delete _buf;
+}
+
+bool RMOptionButton::doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick) {
+ if (!_bDoubleState) {
+ if (_rect.ptInRect(mousePos)) {
+ if (!_bActive) {
+ _bActive = true;
+ return true;
+ }
+ } else {
+ if (_bActive) {
+ _bActive = false;
+ return true;
+ }
+ }
+ } else {
+ if (bLeftClick && _rect.ptInRect(mousePos)) {
+ _bActive = !_bActive;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void RMOptionButton::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!_bActive)
+ return;
+
+ if (_bHasGfx)
+ CORO_INVOKE_2(_buf->draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+void RMOptionButton::addToList(RMGfxTargetBuffer &bigBuf) {
+ if (_bHasGfx)
+ bigBuf.addPrim(new RMGfxPrimitive(this, _rect));
+}
+
+bool RMOptionButton::isActive() {
+ return _bActive;
+}
+
+void RMOptionButton::setActiveState(bool bState) {
+ _bActive = bState;
+}
+
+/****************************************************************************\
+* RMOptionSlide Methods
+\****************************************************************************/
+
+RMOptionSlide::RMOptionSlide(const RMPoint &pt, int nRange, int nStartValue, int slideSize) {
+ RMResRaw *raw;
+
+ _pos = pt;
+ _nSlideSize = slideSize;
+ _nMax = nRange;
+ _nStep = 100 / _nMax;
+ _nValue = nStartValue;
+
+ _sliderCenter = NULL;
+ _sliderLeft = NULL;
+ _sliderRight = NULL;
+ _sliderSingle = NULL;
+
+ // Sliders
+ INIT_GFX16_FROMRAW(20029, _sliderCenter);
+ INIT_GFX16_FROMRAW(20030, _sliderLeft);
+ INIT_GFX16_FROMRAW(20031, _sliderRight);
+ INIT_GFX16_FROMRAW(20032, _sliderSingle);
+
+ // Buttons
+ _pushLeft = new RMOptionButton(RMRect(pt._x - 23, pt._y, pt._x - 23 + 22, pt._y + 26));
+ _pushRight = new RMOptionButton(RMRect(pt._x + _nSlideSize, pt._y, pt._x + _nSlideSize + 5 + 22, pt._y + 26));
+}
+
+
+RMOptionSlide::~RMOptionSlide() {
+ delete _sliderCenter;
+ _sliderCenter = NULL;
+ delete _sliderLeft;
+ _sliderLeft = NULL;
+ delete _sliderRight;
+ _sliderRight = NULL;
+ delete _sliderSingle;
+ _sliderSingle = NULL;
+
+ delete _pushLeft;
+ _pushLeft = NULL;
+ delete _pushRight;
+ _pushRight = NULL;
+}
+
+bool RMOptionSlide::doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick) {
+ bool bRefresh = false;
+
+ // Do the button DoFrame's
+ _pushLeft->doFrame(mousePos, bLeftClick, bRightClick);
+ _pushRight->doFrame(mousePos, bLeftClick, bRightClick);
+
+ if (_pushLeft->isActive()) {
+ if (bLeftClick) {
+ bRefresh = true;
+ _nValue--;
+ } else if (bRightClick) {
+ bRefresh = true;
+ _nValue -= 3;
+ }
+ if (_nValue < 1)
+ _nValue = 1;
+ } else if (_pushRight->isActive()) {
+ bRefresh = true;
+
+ if (bLeftClick) {
+ bRefresh = true;
+ _nValue++;
+ } else if (bRightClick) {
+ bRefresh = true;
+ _nValue += 3;
+ }
+ if (_nValue > _nMax)
+ _nValue = _nMax;
+ }
+
+ return bRefresh;
+}
+
+void RMOptionSlide::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ int val;
+ RMPoint pos;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->pos = _pos;
+ _ctx->pos._x += 4;
+ _ctx->pos._y += 4;
+
+ _ctx->val = _nValue * _nStep;
+ if (_ctx->val < 1)
+ _ctx->val = 1;
+ else if (_ctx->val > 100)
+ _ctx->val = 100;
+
+ if (_ctx->val == 1) {
+ prim->setDst(_ctx->pos);
+ CORO_INVOKE_2(_sliderSingle->draw, bigBuf, prim);
+ } else {
+ prim->setDst(_ctx->pos);
+ CORO_INVOKE_2(_sliderLeft->draw, bigBuf, prim);
+ _ctx->pos._x += 3;
+
+ for (_ctx->i = 1; _ctx->i < _ctx->val - 1; _ctx->i++) {
+ prim->setDst(_ctx->pos);
+ CORO_INVOKE_2(_sliderCenter->draw, bigBuf, prim);
+ _ctx->pos._x += 3;
+ }
+
+ prim->setDst(_ctx->pos);
+ CORO_INVOKE_2(_sliderRight->draw, bigBuf, prim);
+ _ctx->pos._x += 3;
+ }
+
+ CORO_END_CODE;
+}
+
+void RMOptionSlide::addToList(RMGfxTargetBuffer &bigBuf) {
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+}
+
+int RMOptionSlide::getValue() {
+ return _nValue;
+}
+
+/****************************************************************************\
+* RMOptionScreen Methods
+\****************************************************************************/
+
+RMOptionScreen::RMOptionScreen() {
+ _nState = MENUNONE;
+ _menu = NULL;
+ _hideLoadSave = NULL;
+ _quitConfirm = NULL;
+ _bQuitConfirm = false;
+
+ create(RM_SX, RM_SY);
+
+ _buttonExit = NULL;
+ _buttonLoad = NULL;
+ _buttonSave = NULL;
+ _buttonGameMenu = NULL;
+ _buttonGfxMenu = NULL;
+ _buttonSoundMenu = NULL;
+ _buttonSave_ArrowLeft = NULL;
+ _buttonSave_ArrowRight = NULL;
+ _bEditSaveName = false;
+
+ for (int i = 0; i < 6; i++) {
+ _curThumb[i] = NULL;
+ _buttonSave_States[i] = NULL;
+ }
+
+ _statePos = 0;
+ _buttonQuitYes = NULL;
+ _buttonQuitNo = NULL;
+ _buttonQuit = NULL;
+ _saveEasy = NULL;
+ _saveHard = NULL;
+ _buttonGfx_Tips = NULL;
+ _buttonSound_DubbingOn = NULL;
+ _buttonSound_MusicOn = NULL;
+ _buttonSound_SFXOn = NULL;
+ _slideTonySpeed = NULL;
+ _slideTextSpeed = NULL;
+ _buttonGame_Lock = NULL;
+ _buttonGfx_Anni30 = NULL;
+ _sliderSound_Music = NULL;
+ _buttonGame_TimerizedText = NULL;
+ _buttonGfx_AntiAlias = NULL;
+ _sliderSound_SFX = NULL;
+ _buttonGame_Scrolling = NULL;
+ _buttonGfx_Sottotitoli = NULL;
+ _sliderSound_Dubbing = NULL;
+ _buttonGame_InterUp = NULL;
+ _buttonGfx_Trans = NULL;
+
+ _fadeStep = 0;
+ _fadeY = 0;
+ _fadeTime = 0;
+ _nEditPos = 0;
+ _nLastState = MENUGAME;
+}
+
+RMOptionScreen::~RMOptionScreen() {
+ closeState();
+}
+
+void RMOptionScreen::refreshAll(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ RMGfxSourceBuffer16 *thumb;
+ RMText *title;
+ RMText *num[6];
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+ clearOT();
+
+ addPrim(new RMGfxPrimitive(_menu));
+
+ if (_bNoLoadSave)
+ addPrim(new RMGfxPrimitive(_hideLoadSave, RMPoint(0, 401)));
+
+ if (_bQuitConfirm) {
+ addPrim(new RMGfxPrimitive(_quitConfirm, RMPoint(270, 200)));
+ _buttonQuitYes->addToList(*this);
+ _buttonQuitNo->addToList(*this);
+ }
+
+ _buttonExit->addToList(*this);
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ _buttonQuit->addToList(*this);
+ _buttonLoad->addToList(*this);
+ _buttonSave->addToList(*this);
+ }
+
+ if (_nState == MENUGAME) {
+ _buttonGame_Lock->addToList(*this);
+ _buttonGame_TimerizedText->addToList(*this);
+ _buttonGame_Scrolling->addToList(*this);
+ _buttonGame_InterUp->addToList(*this);
+ _slideTextSpeed->addToList(*this);
+ _slideTonySpeed->addToList(*this);
+ } else if (_nState == MENUGFX) {
+ _buttonGfx_Anni30->addToList(*this);
+ _buttonGfx_AntiAlias->addToList(*this);
+ _buttonGfx_Sottotitoli->addToList(*this);
+ _buttonGfx_Trans->addToList(*this);
+ _buttonGfx_Tips->addToList(*this);
+ } else if (_nState == MENUSOUND) {
+ _sliderSound_Dubbing->addToList(*this);
+ _sliderSound_Music->addToList(*this);
+ _sliderSound_SFX->addToList(*this);
+ _buttonSound_DubbingOn->addToList(*this);
+ _buttonSound_MusicOn->addToList(*this);
+ _buttonSound_SFXOn->addToList(*this);
+ }
+
+ _ctx->thumb = NULL;
+ _ctx->title = NULL;
+ Common::fill(&_ctx->num[0], &_ctx->num[6], (RMText *)NULL);
+
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ _ctx->title = new RMText;
+ if (_nState == MENULOAD) {
+ RMMessage msg(10);
+ _ctx->title->writeText(msg[0], 1);
+ } else {
+ RMMessage msg(11);
+ _ctx->title->writeText(msg[0], 1);
+ }
+
+ addPrim(new RMGfxPrimitive(_ctx->title, RMPoint(320, 10)));
+
+ if (_curThumbDiff[0] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(48, 57)));
+ else if (_curThumbDiff[0] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(48, 57)));
+ if (_curThumbDiff[1] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(240, 57)));
+ else if (_curThumbDiff[1] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(240, 57)));
+ if (_curThumbDiff[2] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(432, 57)));
+ else if (_curThumbDiff[2] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(432, 57)));
+ if (_curThumbDiff[3] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(48, 239)));
+ else if (_curThumbDiff[3] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(48, 239)));
+ if (_curThumbDiff[4] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(240, 239)));
+ else if (_curThumbDiff[4] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(240, 239)));
+ if (_curThumbDiff[5] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(432, 239)));
+ else if (_curThumbDiff[5] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(432, 239)));
+
+ if (_curThumb[0] && !(_bEditSaveName && _nEditPos == 0))
+ addPrim(new RMGfxPrimitive(_curThumb[0], RMPoint(48, 57)));
+ if (_curThumb[1] && !(_bEditSaveName && _nEditPos == 1))
+ addPrim(new RMGfxPrimitive(_curThumb[1], RMPoint(240, 57)));
+ if (_curThumb[2] && !(_bEditSaveName && _nEditPos == 2))
+ addPrim(new RMGfxPrimitive(_curThumb[2], RMPoint(432, 57)));
+ if (_curThumb[3] && !(_bEditSaveName && _nEditPos == 3))
+ addPrim(new RMGfxPrimitive(_curThumb[3], RMPoint(48, 239)));
+ if (_curThumb[4] && !(_bEditSaveName && _nEditPos == 4))
+ addPrim(new RMGfxPrimitive(_curThumb[4], RMPoint(240, 239)));
+ if (_curThumb[5] && !(_bEditSaveName && _nEditPos == 5))
+ addPrim(new RMGfxPrimitive(_curThumb[5], RMPoint(432, 239)));
+
+ if (_bEditSaveName) {
+ _ctx->thumb = new RMGfxSourceBuffer16;
+ _ctx->thumb->init((byte *)g_vm->getThumbnail(), 640 / 4, 480 / 4);
+
+ if (_nEditPos == 0)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(48, 57)));
+ else if (_nEditPos == 1)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(240, 57)));
+ else if (_nEditPos == 2)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(432, 57)));
+ else if (_nEditPos == 3)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(48, 239)));
+ else if (_nEditPos == 4)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(240, 239)));
+ else if (_nEditPos == 5)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(432, 239)));
+ }
+
+ for (_ctx->i = 0; _ctx->i < 6; _ctx->i++) {
+ Common::String s;
+
+ if (_bEditSaveName && _nEditPos == _ctx->i)
+ s = Common::String::format("%02d)%s*", _statePos + _ctx->i, _editName);
+ else {
+ if (_statePos == 0 && _ctx->i == 0)
+ s = "Autosave";
+ else
+ s = Common::String::format("%02d)%s", _statePos + _ctx->i, _curThumbName[_ctx->i].c_str());
+ }
+
+ _ctx->num[_ctx->i] = new RMText;
+ _ctx->num[_ctx->i]->setAlignType(RMText::HLEFT, RMText::VTOP);
+ _ctx->num[_ctx->i]->writeText(s, 2);
+ }
+
+ addPrim(new RMGfxPrimitive(_ctx->num[0], RMPoint(55 - 3, 180 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[1], RMPoint(247 - 3, 180 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[2], RMPoint(439 - 3, 180 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[3], RMPoint(55 - 3, 362 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[4], RMPoint(247 - 3, 362 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[5], RMPoint(439 - 3, 362 + 14)));
+
+ _buttonSave_ArrowLeft->addToList(*this);
+ _buttonSave_ArrowRight->addToList(*this);
+ }
+
+ CORO_INVOKE_0(drawOT);
+
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ if (_ctx->thumb)
+ delete _ctx->thumb;
+
+ if (_ctx->title)
+ delete _ctx->title;
+
+ for (_ctx->i = 0; _ctx->i < 6; _ctx->i++) {
+ if (_ctx->num[_ctx->i])
+ delete _ctx->num[_ctx->i];
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::refreshThumbnails() {
+ for (int i = 0; i < 6; i++) {
+ if (_curThumb[i])
+ delete _curThumb[i];
+
+ _curThumb[i] = new RMGfxSourceBuffer16;
+ _curThumb[i]->create(640 / 4, 480 / 4);
+ if (!loadThumbnailFromSaveState(_statePos + i, *_curThumb[i], _curThumbName[i], _curThumbDiff[i])) {
+ delete _curThumb[i];
+ _curThumb[i] = NULL;
+ _curThumbName[i].clear();
+ _curThumbDiff[i] = 11;
+ }
+ }
+}
+
+void RMOptionScreen::initState(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ RMResRaw *raw;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND)
+ _ctx->raw = new RMResRaw(20000 + _nState);
+ else if (_nState == MENULOAD || _nState == MENUSAVE) {
+ if (_bAlterGfx)
+ _ctx->raw = new RMResRaw(20024);
+ else
+ _ctx->raw = new RMResRaw(20003);
+ } else {
+ error("Invalid state");
+ }
+
+ assert(_ctx->raw->isValid());
+ assert(_menu == NULL);
+ _menu = new RMGfxSourceBuffer16(false);
+ _menu->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height());
+ delete _ctx->raw;
+
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ if (_bAlterGfx) {
+ assert(_buttonExit == NULL);
+ _buttonExit = new RMOptionButton(20025, RMPoint(561, 406));
+ } else {
+ assert(_buttonExit == NULL);
+ _buttonExit = new RMOptionButton(20012, RMPoint(560, 404));
+ }
+
+ INIT_GFX8_FROMRAW(_ctx->raw, 20036, _saveEasy);
+ INIT_GFX8_FROMRAW(_ctx->raw, 20037, _saveHard);
+
+ refreshThumbnails();
+
+ assert(_buttonSave_States[0] == NULL);
+ _buttonSave_States[0] = new RMOptionButton(RMRect(48, 57, 48 + 160, 57 + 120));
+ assert(_buttonSave_States[1] == NULL);
+ _buttonSave_States[1] = new RMOptionButton(RMRect(240, 57, 240 + 160, 57 + 120));
+ assert(_buttonSave_States[2] == NULL);
+ _buttonSave_States[2] = new RMOptionButton(RMRect(432, 57, 432 + 160, 57 + 120));
+ assert(_buttonSave_States[3] == NULL);
+ _buttonSave_States[3] = new RMOptionButton(RMRect(48, 239, 48 + 160, 239 + 120));
+ assert(_buttonSave_States[4] == NULL);
+ _buttonSave_States[4] = new RMOptionButton(RMRect(240, 239, 240 + 160, 239 + 120));
+ assert(_buttonSave_States[5] == NULL);
+ _buttonSave_States[5] = new RMOptionButton(RMRect(432, 239, 432 + 160, 239 + 120));
+
+ if (_bAlterGfx) {
+ assert(_buttonSave_ArrowLeft == NULL);
+ _buttonSave_ArrowLeft = new RMOptionButton(20026, RMPoint(3, 196));
+ assert(_buttonSave_ArrowRight == NULL);
+ _buttonSave_ArrowRight = new RMOptionButton(20027, RMPoint(601, 197));
+ } else {
+ assert(_buttonSave_ArrowLeft == NULL);
+ _buttonSave_ArrowLeft = new RMOptionButton(20013, RMPoint(0, 197));
+ assert(_buttonSave_ArrowRight == NULL);
+ _buttonSave_ArrowRight = new RMOptionButton(20014, RMPoint(601, 197));
+ }
+ } else if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ assert(_buttonExit == NULL);
+ _buttonExit = new RMOptionButton(20005, RMPoint(560, 405));
+ assert(_buttonQuit == NULL);
+ _buttonQuit = new RMOptionButton(20020, RMPoint(7, 408));
+ assert(_buttonLoad == NULL);
+ _buttonLoad = new RMOptionButton(20006, RMPoint(231, 401));
+ assert(_buttonSave == NULL);
+ _buttonSave = new RMOptionButton(20007, RMPoint(325, 401));
+
+ assert(_buttonGameMenu == NULL);
+ _buttonGameMenu = new RMOptionButton(RMRect(24, 32, 118, 64));
+ assert(_buttonGfxMenu == NULL);
+ _buttonGfxMenu = new RMOptionButton(RMRect(118, 32, 212, 64));
+ assert(_buttonSoundMenu == NULL);
+ _buttonSoundMenu = new RMOptionButton(RMRect(212, 32, 306, 64));
+
+ _ctx->raw = new RMResRaw(20021);
+ assert(_ctx->raw->isValid());
+ assert(_quitConfirm == NULL);
+ _quitConfirm = new RMGfxSourceBuffer16(false);
+ _quitConfirm->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height());
+ delete _ctx->raw;
+
+ assert(_buttonQuitYes == NULL);
+ _buttonQuitYes = new RMOptionButton(20022, RMPoint(281, 265));
+ _buttonQuitYes->setPriority(30);
+ assert(_buttonQuitNo == NULL);
+ _buttonQuitNo = new RMOptionButton(20023, RMPoint(337, 264));
+ _buttonQuitNo->setPriority(30);
+
+ if (_bNoLoadSave) {
+ _ctx->raw = new RMResRaw(20028);
+ assert(_ctx->raw->isValid());
+ assert(_hideLoadSave == NULL);
+ _hideLoadSave = new RMGfxSourceBuffer16(false);
+ _hideLoadSave->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height());
+ delete _ctx->raw;
+ }
+
+ // Menu GAME
+ if (_nState == MENUGAME) {
+ assert(_buttonGame_Lock == NULL);
+ _buttonGame_Lock = new RMOptionButton(20008, RMPoint(176, 262), true);
+ _buttonGame_Lock->setActiveState(GLOBALS._bCfgInvLocked);
+ assert(_buttonGame_TimerizedText == NULL);
+ _buttonGame_TimerizedText = new RMOptionButton(20009, RMPoint(463, 273), true);
+ _buttonGame_TimerizedText->setActiveState(!GLOBALS._bCfgTimerizedText);
+ assert(_buttonGame_Scrolling == NULL);
+ _buttonGame_Scrolling = new RMOptionButton(20010, RMPoint(315, 263), true);
+ _buttonGame_Scrolling->setActiveState(GLOBALS._bCfgInvNoScroll);
+ assert(_buttonGame_InterUp == NULL);
+ _buttonGame_InterUp = new RMOptionButton(20011, RMPoint(36, 258), true);
+ _buttonGame_InterUp->setActiveState(GLOBALS._bCfgInvUp);
+
+ assert(_slideTextSpeed == NULL);
+ _slideTextSpeed = new RMOptionSlide(RMPoint(165, 122), 10, GLOBALS._nCfgTextSpeed);
+ assert(_slideTonySpeed == NULL);
+ _slideTonySpeed = new RMOptionSlide(RMPoint(165, 226), 5, GLOBALS._nCfgTonySpeed);
+ }
+ // Menu Graphics
+ else if (_nState == MENUGFX) {
+ assert(_buttonGfx_Anni30 == NULL);
+ _buttonGfx_Anni30 = new RMOptionButton(20015, RMPoint(247, 178), true);
+ _buttonGfx_Anni30->setActiveState(GLOBALS._bCfgAnni30);
+ assert(_buttonGfx_AntiAlias == NULL);
+ _buttonGfx_AntiAlias = new RMOptionButton(20016, RMPoint(430, 83), true);
+ _buttonGfx_AntiAlias->setActiveState(!GLOBALS._bCfgAntiAlias);
+ assert(_buttonGfx_Sottotitoli == NULL);
+ _buttonGfx_Sottotitoli = new RMOptionButton(20017, RMPoint(98, 82), true);
+ _buttonGfx_Sottotitoli->setActiveState(!GLOBALS._bShowSubtitles);
+ assert(_buttonGfx_Tips == NULL);
+ _buttonGfx_Tips = new RMOptionButton(20018, RMPoint(431, 246), true);
+ _buttonGfx_Tips->setActiveState(GLOBALS._bCfgInterTips);
+ assert(_buttonGfx_Trans == NULL);
+ _buttonGfx_Trans = new RMOptionButton(20019, RMPoint(126, 271), true);
+ _buttonGfx_Trans->setActiveState(!GLOBALS._bCfgTransparence);
+
+ } else if (_nState == MENUSOUND) {
+ assert(_sliderSound_Dubbing == NULL);
+ _sliderSound_Dubbing = new RMOptionSlide(RMPoint(165, 122), 10, GLOBALS._nCfgDubbingVolume);
+ assert(_sliderSound_Music == NULL);
+ _sliderSound_Music = new RMOptionSlide(RMPoint(165, 226), 10, GLOBALS._nCfgMusicVolume);
+ assert(_sliderSound_SFX == NULL);
+ _sliderSound_SFX = new RMOptionSlide(RMPoint(165, 330), 10, GLOBALS._nCfgSFXVolume);
+
+ assert(_buttonSound_DubbingOn == NULL);
+ _buttonSound_DubbingOn = new RMOptionButton(20033, RMPoint(339, 75), true);
+ _buttonSound_DubbingOn->setActiveState(GLOBALS._bCfgDubbing);
+ assert(_buttonSound_MusicOn == NULL);
+ _buttonSound_MusicOn = new RMOptionButton(20034, RMPoint(338, 179), true);
+ _buttonSound_MusicOn->setActiveState(GLOBALS._bCfgMusic);
+ assert(_buttonSound_SFXOn == NULL);
+ _buttonSound_SFXOn = new RMOptionButton(20035, RMPoint(338, 283), true);
+ _buttonSound_SFXOn->setActiveState(GLOBALS._bCfgSFX);
+ }
+ }
+
+ CORO_INVOKE_0(refreshAll);
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::closeState() {
+ delete _menu;
+ _menu = NULL;
+
+ delete _buttonExit;
+ _buttonExit = NULL;
+
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ for (int i = 0; i < 6; i++) {
+ if (_curThumb[i] != NULL) {
+ delete _curThumb[i];
+ _curThumb[i] = NULL;
+ }
+
+ delete _buttonSave_States[i];
+ _buttonSave_States[i] = NULL;
+ }
+
+ delete _buttonSave_ArrowLeft;
+ _buttonSave_ArrowLeft = NULL;
+ delete _buttonSave_ArrowRight;
+ _buttonSave_ArrowRight = NULL;
+
+ delete _saveEasy;
+ _saveEasy = NULL;
+ delete _saveHard;
+ _saveHard = NULL;
+ }
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ delete _buttonQuit;
+ _buttonQuit = NULL;
+ delete _buttonLoad;
+ _buttonLoad = NULL;
+ delete _buttonSave;
+ _buttonSave = NULL;
+ delete _buttonGameMenu;
+ _buttonGameMenu = NULL;
+ delete _buttonGfxMenu;
+ _buttonGfxMenu = NULL;
+ delete _buttonSoundMenu;
+ _buttonSoundMenu = NULL;
+ delete _quitConfirm;
+ _quitConfirm = NULL;
+ delete _buttonQuitYes;
+ _buttonQuitYes = NULL;
+ delete _buttonQuitNo;
+ _buttonQuitNo = NULL;
+
+ if (_bNoLoadSave) {
+ delete _hideLoadSave;
+ _hideLoadSave = NULL;
+ }
+
+ if (_nState == MENUGAME) {
+ GLOBALS._bCfgInvLocked = _buttonGame_Lock->isActive();
+ delete _buttonGame_Lock;
+ _buttonGame_Lock = NULL;
+
+ GLOBALS._bCfgTimerizedText = !_buttonGame_TimerizedText->isActive();
+ delete _buttonGame_TimerizedText;
+ _buttonGame_TimerizedText = NULL;
+
+ GLOBALS._bCfgInvNoScroll = _buttonGame_Scrolling->isActive();
+ delete _buttonGame_Scrolling;
+ _buttonGame_Scrolling = NULL;
+
+ GLOBALS._bCfgInvUp = _buttonGame_InterUp->isActive();
+ delete _buttonGame_InterUp;
+ _buttonGame_InterUp = NULL;
+
+ GLOBALS._nCfgTextSpeed = _slideTextSpeed->getValue();
+ delete _slideTextSpeed;
+ _slideTextSpeed = NULL;
+
+ GLOBALS._nCfgTonySpeed = _slideTonySpeed->getValue();
+ delete _slideTonySpeed;
+ _slideTonySpeed = NULL;
+ } else if (_nState == MENUGFX) {
+ GLOBALS._bCfgAnni30 = _buttonGfx_Anni30->isActive();
+ delete _buttonGfx_Anni30;
+ _buttonGfx_Anni30 = NULL;
+
+ GLOBALS._bCfgAntiAlias = !_buttonGfx_AntiAlias->isActive();
+ delete _buttonGfx_AntiAlias;
+ _buttonGfx_AntiAlias = NULL;
+
+ GLOBALS._bShowSubtitles = !_buttonGfx_Sottotitoli->isActive();
+ delete _buttonGfx_Sottotitoli;
+ _buttonGfx_Sottotitoli = NULL;
+
+ GLOBALS._bCfgInterTips = _buttonGfx_Tips->isActive();
+ delete _buttonGfx_Tips;
+ _buttonGfx_Tips = NULL;
+
+ GLOBALS._bCfgTransparence = !_buttonGfx_Trans->isActive();
+ delete _buttonGfx_Trans;
+ _buttonGfx_Trans = NULL;
+ } else if (_nState == MENUSOUND) {
+ GLOBALS._nCfgDubbingVolume = _sliderSound_Dubbing->getValue();
+ delete _sliderSound_Dubbing;
+ _sliderSound_Dubbing = NULL;
+
+ GLOBALS._nCfgMusicVolume = _sliderSound_Music->getValue();
+ delete _sliderSound_Music;
+ _sliderSound_Music = NULL;
+
+ GLOBALS._nCfgSFXVolume = _sliderSound_SFX->getValue();
+ delete _sliderSound_SFX;
+ _sliderSound_SFX = NULL;
+
+ GLOBALS._bCfgDubbing = _buttonSound_DubbingOn->isActive();
+ delete _buttonSound_DubbingOn;
+ _buttonSound_DubbingOn = NULL;
+
+ GLOBALS._bCfgMusic = _buttonSound_MusicOn->isActive();
+ delete _buttonSound_MusicOn;
+ _buttonSound_MusicOn = NULL;
+
+ GLOBALS._bCfgSFX = _buttonSound_SFXOn->isActive();
+ delete _buttonSound_SFXOn;
+ _buttonSound_SFXOn = NULL;
+ }
+
+ // Save the new settings to ScummVM
+ g_vm->saveSoundSettings();
+ }
+
+ _nState = MENUNONE;
+}
+
+void RMOptionScreen::reInit(RMGfxTargetBuffer &bigBuf) {
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+}
+
+void RMOptionScreen::init(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_fadeStep != 0) {
+ result = false;
+ return;
+ }
+
+ _fadeStep = 1;
+ _fadeY = -20;
+ _fadeTime = -1;
+ _bExit = false;
+ _bLoadMenuOnly = false;
+ _bNoLoadSave = false;
+ _bAlterGfx = false;
+
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ if (_nState == MENULOAD || _nState == MENUSAVE || _nState == MENUNONE)
+ _nState = MENUGAME;
+
+ CORO_INVOKE_0(initState);
+
+ result = true;
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::initLoadMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_fadeStep != 0) {
+ result = false;
+ return;
+ }
+
+ _fadeStep = 1;
+ _fadeY = -20;
+ _fadeTime = -1;
+ _bExit = false;
+ _bLoadMenuOnly = true;
+ _bNoLoadSave = false;
+ _bAlterGfx = bAlternateGfx;
+
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ _nState = MENULOAD;
+ CORO_INVOKE_0(initState);
+
+ result = true;
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::initSaveMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_fadeStep != 0) {
+ result = false;
+ return;
+ }
+
+ _fadeStep = 1;
+ _fadeY = -20;
+ _fadeTime = -1;
+ _bExit = false;
+ _bLoadMenuOnly = true;
+ _bNoLoadSave = false;
+ _bAlterGfx = bAlternateGfx;
+
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ _nState = MENUSAVE;
+ CORO_INVOKE_0(initState);
+
+ result = true;
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::initNoLoadSave(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_fadeStep != 0) {
+ result = false;
+ return;
+ }
+
+ _fadeStep = 1;
+ _fadeY = -20;
+ _fadeTime = -1;
+ _bExit = false;
+ _bLoadMenuOnly = false;
+ _bNoLoadSave = true;
+
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ _nState = MENUGAME;
+ CORO_INVOKE_0(initState);
+
+ result = true;
+
+ CORO_END_CODE;
+}
+
+bool RMOptionScreen::close() {
+ if (_fadeStep != 6)
+ return false;
+
+ // Start fade out
+ _fadeStep++;
+ _fadeTime = g_vm->getTime();
+ return true;
+}
+
+bool RMOptionScreen::isClosing() {
+ return _bExit;
+}
+
+int RMOptionScreen::priority() {
+ // Just below the mouse
+ return 190;
+}
+
+void RMOptionScreen::changeState(CORO_PARAM, OptionScreenState newState) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _nLastState = _nState;
+ closeState();
+ _nState = newState;
+ CORO_INVOKE_0(initState);
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::doFrame(CORO_PARAM, RMInput *input) {
+ CORO_BEGIN_CONTEXT;
+ bool bLeftClick, bRightClick;
+ RMPoint mousePos;
+ bool bRefresh;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+
+ // If it is fully open, do nothing
+ if (_fadeStep != 6)
+ return;
+
+ // Reads input
+ _ctx->mousePos = input->mousePos();
+ _ctx->bLeftClick = input->mouseLeftClicked();
+ _ctx->bRightClick = input->mouseRightClicked();
+
+ _ctx->bRefresh = false;
+
+ if (_bQuitConfirm) {
+ _ctx->bRefresh |= _buttonQuitYes->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonQuitNo->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ } else {
+ _ctx->bRefresh |= _buttonExit->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ // Check if you have clicked on the output
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ // Buttons without graphics...
+ _buttonGameMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _buttonGfxMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _buttonSoundMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ // Buttons with graphics
+ if (!_bNoLoadSave) {
+ if (!g_vm->getIsDemo()) {
+ _ctx->bRefresh |= _buttonLoad->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonSave->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ }
+
+ _ctx->bRefresh |= _buttonQuit->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ }
+ }
+
+ if (_nState == MENUGAME) {
+ _ctx->bRefresh |= _buttonGame_Lock->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGame_TimerizedText->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGame_Scrolling->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGame_InterUp->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _slideTextSpeed->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _slideTonySpeed->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ } else if (_nState == MENUGFX) {
+ _ctx->bRefresh |= _buttonGfx_Anni30->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGfx_AntiAlias->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGfx_Sottotitoli->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGfx_Tips->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGfx_Trans->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ } else if (_nState == MENUSOUND) {
+ _ctx->bRefresh |= _sliderSound_Dubbing->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _sliderSound_Music->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _sliderSound_SFX->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonSound_DubbingOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonSound_MusicOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonSound_SFXOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ } else if (_nState == MENULOAD || _nState == MENUSAVE) {
+ for (_ctx->i = 0; _ctx->i < 6; _ctx->i++)
+ _buttonSave_States[_ctx->i]->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ if (_statePos > 0)
+ _ctx->bRefresh |= _buttonSave_ArrowLeft->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ if (_statePos < 90)
+ _ctx->bRefresh |= _buttonSave_ArrowRight->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ }
+ }
+
+#define KEYPRESS(c) (g_vm->getEngine()->getInput().getAsyncKeyState(c))
+#define PROCESS_CHAR(cod, c) if (KEYPRESS(cod)) { \
+ _editName[strlen(_editName) + 1] = '\0'; _editName[strlen(_editName)] = c; _ctx->bRefresh = true; }
+
+ // State Buttons
+ if (_bEditSaveName) {
+ if (KEYPRESS(Common::KEYCODE_BACKSPACE)) {
+ if (_editName[0] != '\0') {
+ _editName[strlen(_editName) - 1] = '\0';
+ _ctx->bRefresh = true;
+ }
+ }
+
+ for (_ctx->i = 0; _ctx->i < 26 && strlen(_editName) < 12; _ctx->i++) {
+ if (KEYPRESS(Common::KEYCODE_LSHIFT) ||
+ KEYPRESS(Common::KEYCODE_RSHIFT)) {
+ PROCESS_CHAR((Common::KeyCode)((int)'a' + _ctx->i), _ctx->i + 'A');
+ } else {
+ PROCESS_CHAR((Common::KeyCode)((int)'a' + _ctx->i), _ctx->i + 'a');
+ }
+ }
+
+ for (_ctx->i = 0; _ctx->i < 10 && strlen(_editName) < 12; _ctx->i++)
+ PROCESS_CHAR((Common::KeyCode)((int)'0' + _ctx->i), _ctx->i + '0');
+
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_SPACE, ' ');
+
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP0, '0');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP1, '1');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP2, '2');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP3, '3');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP4, '4');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP5, '5');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP6, '6');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP7, '7');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP8, '8');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP9, '9');
+
+ // Cancel
+ if (KEYPRESS(Common::KEYCODE_ESCAPE)) {
+ _bEditSaveName = false;
+ _ctx->bRefresh = true;
+ }
+
+ // OK
+ if (KEYPRESS(Common::KEYCODE_RETURN)) {
+ _bEditSaveName = false;
+ g_vm->saveState(_statePos + _nEditPos, _editName);
+ close();
+ }
+
+ } else if (_ctx->bLeftClick) {
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ if (_buttonExit->isActive()) {
+ if (_bLoadMenuOnly) {
+ // If only the loading menu, close
+ close();
+ } else {
+ CORO_INVOKE_1(changeState, _nLastState);
+ _ctx->bRefresh = true;
+ }
+ } else if (_buttonSave_ArrowLeft->isActive()) {
+ if (_statePos > 0) {
+ _statePos -= 6;
+ if (_statePos < 0)
+ _statePos = 0;
+ _buttonSave_ArrowLeft->setActiveState(false);
+ _ctx->bRefresh = true;
+ refreshThumbnails();
+ }
+ } else if (_buttonSave_ArrowRight->isActive()) {
+ if (_statePos < 90) {
+ _statePos += 6;
+ if (_statePos > 90)
+ _statePos = 90;
+ _buttonSave_ArrowRight->setActiveState(false);
+ _ctx->bRefresh = true;
+ refreshThumbnails();
+ }
+ } else {
+ for (_ctx->i = 0; _ctx->i < 6; _ctx->i++)
+ if (_buttonSave_States[_ctx->i]->isActive()) {
+ // There by saving or loading!!!
+ if (_nState == MENULOAD && _curThumb[_ctx->i] != NULL) {
+ // Loading
+ CORO_INVOKE_1(g_vm->loadState, _statePos + _ctx->i);
+ close();
+ } else if (_nState == MENUSAVE && (_statePos != 0 || _ctx->i != 0)) {
+ // Turn on edit mode
+ _bEditSaveName = true;
+ _nEditPos = _ctx->i;
+ strcpy(_editName, _curThumbName[_ctx->i].c_str());
+ _ctx->bRefresh = true;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ if (_bQuitConfirm) {
+ if (_buttonQuitNo->isActive()) {
+ _bQuitConfirm = false;
+ _ctx->bRefresh = true;
+ } else if (_buttonQuitYes->isActive()) {
+ _bQuitConfirm = false;
+ _ctx->bRefresh = true;
+
+ g_vm->quitGame();
+ }
+ } else {
+ if (_buttonQuit->isActive()) {
+ _bQuitConfirm = true;
+ _buttonQuitNo->setActiveState(false);
+ _buttonQuitYes->setActiveState(false);
+ _ctx->bRefresh = true;
+ } else if (_buttonExit->isActive())
+ close();
+ else if (_buttonLoad->isActive()) {
+ CORO_INVOKE_1(changeState, MENULOAD);
+ _ctx->bRefresh = true;
+ } else if (_buttonSave->isActive()) {
+ CORO_INVOKE_1(changeState, MENUSAVE);
+ _ctx->bRefresh = true;
+ } else if (_buttonGameMenu->isActive() && _nState != MENUGAME) {
+ CORO_INVOKE_1(changeState, MENUGAME);
+ _ctx->bRefresh = true;
+ } else if (_buttonGfxMenu->isActive() && _nState != MENUGFX) {
+ CORO_INVOKE_1(changeState, MENUGFX);
+ _ctx->bRefresh = true;
+ } else if (_buttonSoundMenu->isActive() && _nState != MENUSOUND) {
+ CORO_INVOKE_1(changeState, MENUSOUND);
+ _ctx->bRefresh = true;
+ }
+
+ if (_nState == MENUGFX) {
+ // These options take effect immediately
+ if (_buttonGfx_Anni30->isActive())
+ GLOBALS._bCfgAnni30 = true;
+ else
+ GLOBALS._bCfgAnni30 = false;
+
+ if (_buttonGfx_AntiAlias->isActive())
+ GLOBALS._bCfgAntiAlias = false;
+ else
+ GLOBALS._bCfgAntiAlias = true;
+
+ if (_buttonGfx_Trans->isActive())
+ GLOBALS._bCfgTransparence = false;
+ else
+ GLOBALS._bCfgTransparence = true;
+ }
+ }
+ }
+ }
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ if (!_bQuitConfirm && KEYPRESS(Common::KEYCODE_ESCAPE))
+ close();
+ }
+
+ if (_ctx->bRefresh)
+ CORO_INVOKE_0(refreshAll);
+
+ CORO_END_CODE;
+}
+
+
+void RMOptionScreen::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ int curTime;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->curTime = g_vm->getTime();
+
+#define FADE_SPEED 20
+#define SYNC (_ctx->curTime - _fadeTime) / 25
+
+ if (_bExit)
+ return;
+
+ if (_fadeStep == 1) {
+ // Downhill fast
+ if (_fadeTime == -1)
+ _fadeY += FADE_SPEED;
+ else
+ _fadeY += FADE_SPEED * SYNC;
+ if (_fadeY > 480) {
+ _fadeY = 480;
+ _fadeStep++;
+ }
+
+ // Set the part to draw the scrolling
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 2) {
+ // Bounce 1
+ _fadeY -= FADE_SPEED / 2 * SYNC;
+ if (_fadeY < 400) {
+ _fadeY = 400;
+ _fadeStep++;
+ }
+
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 3) {
+ _fadeY -= FADE_SPEED / 4 * SYNC;
+ if (_fadeY < 380) {
+ _fadeY = 380;
+ _fadeStep++;
+ }
+
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 4) {
+ // Bounce 1 - 2
+ _fadeY += FADE_SPEED / 3 * SYNC;
+ if (_fadeY > 420) {
+ _fadeY = 420;
+ _fadeStep++;
+ }
+
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 5) {
+ _fadeY += FADE_SPEED / 2 * SYNC;
+ if (_fadeY > 480) {
+ _fadeY = 480;
+ _fadeStep++;
+ g_vm->hideLocation();
+ }
+
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 6) {
+ // Menu ON
+
+ } else if (_fadeStep == 7) {
+ // Menu OFF
+ g_vm->showLocation();
+ _fadeStep++;
+
+ } else if (_fadeStep == 8) {
+ _fadeY -= FADE_SPEED * SYNC;
+ if (_fadeY < 0) {
+ _fadeY = 0;
+ _fadeStep++;
+ }
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 9) {
+ // Hello hello!
+ _bExit = true;
+ _fadeStep = 0;
+
+ // Free memory
+ closeState();
+ return;
+
+ } else {
+ _fadeStep = 0;
+ }
+
+ _fadeTime = _ctx->curTime;
+
+ CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::removeThis(CORO_PARAM, bool &result) {
+ if (_bExit)
+ result = true;
+ else
+ result = false;
+}
+
+
+bool RMOptionScreen::loadThumbnailFromSaveState(int nState, byte *lpDestBuf, Common::String &name, byte &diff) {
+ char namebuf[256];
+ Common::InSaveFile *f;
+ char id[4];
+
+ // Cleans the destination
+ Common::fill(lpDestBuf, lpDestBuf + 160 * 120 * 2, 0);
+ name = "No name";
+ diff = 10;
+
+ // Get the savegame filename for the given slot
+ Common::String buf = g_vm->getSaveStateFileName(nState);
+
+ // Try and open the savegame
+ f = g_system->getSavefileManager()->openForLoading(buf);
+ if (f == NULL)
+ return false;
+
+ // Check to see if the file has a valid header
+ f->read(id, 4);
+ if (id[0] != 'R' || id[1] != 'M' || id[2] != 'S') {
+ delete f;
+ return false;
+ }
+
+ if (id[3] < 0x3) {
+ // Very old version that doesn't have screenshots
+ delete f;
+ return true;
+ }
+
+ // Load the screenshot
+ if ((id[3] >= 0x5) && (id[3] < 0x8)) {
+ // Read it as an LZO compressed data block
+ byte *cmpbuf;
+ uint32 cmpsize, size;
+
+ cmpbuf = new byte[160 * 120 * 4];
+
+ // Read in the compressed data
+ cmpsize = f->readUint32LE();
+ f->read(cmpbuf, cmpsize);
+
+ lzo1x_decompress(cmpbuf, cmpsize, lpDestBuf, &size);
+
+ delete[] cmpbuf;
+ } else {
+ // Read in the screenshot as an uncompressed data block
+ if (id[3] >= 8)
+ // Recent versions use hardcoded 160x120 uncomrpessed data, so size can be skipped
+ f->skip(4);
+
+ f->read(lpDestBuf, 160 * 120 * 2);
+ }
+
+ if (id[3] >= 0x5) {
+ // Read in the difficulty level
+ diff = f->readByte();
+ }
+
+ if (id[3] < 0x4) {
+ // Savegame version doesn't have a stored name
+ delete f;
+ return true;
+ }
+
+ int bufSize = f->readByte();
+ f->read(namebuf, bufSize);
+ namebuf[bufSize] = '\0';
+ name = namebuf;
+
+ delete f;
+ return true;
+}
+
+/****************************************************************************\
+* RMPointer Methods
+\****************************************************************************/
+
+RMPointer::RMPointer() {
+ Common::fill(_pointer, _pointer + 16, (RMGfxSourceBuffer8 *)NULL);
+ Common::fill(_specialPointer, _specialPointer + 16, (RMItem *)NULL);
+
+ _nCurPointer = _nCurSpecialPointer = 0;
+ _nCurCustomPointer = NULL;
+}
+
+RMPointer::~RMPointer() {
+ close();
+}
+
+void RMPointer::init() {
+ for (int i = 0; i < 5; i++) {
+ RMResRaw res(RES_P_GO + i);
+
+ _pointer[i] = new RMGfxSourceBuffer8RLEByteAA;
+ _pointer[i]->init(res, res.width(), res.height(), false);
+ _pointer[i]->loadPaletteWA(RES_P_PAL);
+ }
+
+ for (int i = 0; i < 5; i++) {
+ RMRes res(RES_P_PAP1 + i);
+ Common::SeekableReadStream *ds = res.getReadStream();
+ _specialPointer[i] = new RMItem;
+ _specialPointer[i]->readFromStream(*ds);
+ delete ds;
+ }
+
+ //m_hotspot[0].set(19,5);
+ _hotspot[0].set(5, 1);
+ _hotspot[1].set(32, 28);
+ _hotspot[2].set(45, 23);
+ _hotspot[3].set(35, 25);
+ _hotspot[4].set(32, 28);
+
+ // Default=GO
+ _nCurPointer = 0;
+ _nCurSpecialPointer = 0;
+}
+
+void RMPointer::close() {
+ for (int i = 0; i < 5; i++) {
+ if (_pointer[i] != NULL) {
+ delete _pointer[i];
+ _pointer[i] = NULL;
+ }
+
+ if (_specialPointer[i] != NULL) {
+ delete _specialPointer[i];
+ _specialPointer[i] = NULL;
+ }
+ }
+}
+
+void RMPointer::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ int n;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Check the pointer
+ _ctx->n = _nCurPointer;
+ if (_ctx->n == TA_COMBINE)
+ _ctx->n = TA_USE;
+
+ _cursorHotspot = _hotspot[_ctx->n];
+
+ // Call the Draw method of the pointer
+ if (_nCurSpecialPointer == 0) {
+ // WORKAROUND: updateCursor gets called too early sometimes (for example, when
+ // the cursor is released over the TA_PERORATE option), via setAction.
+ if (_ctx->n > 4)
+ _ctx->n = 0;
+
+ CORO_INVOKE_2(_pointer[_ctx->n]->draw, bigBuf, prim);
+ } else {
+ if (_nCurSpecialPointer == PTR_CUSTOM)
+ CORO_INVOKE_2(_nCurCustomPointer->draw, bigBuf, prim);
+ else
+ // Call the draw on the special pointer
+ CORO_INVOKE_2(_specialPointer[_nCurSpecialPointer - 1]->draw, bigBuf, prim);
+ }
+
+ CORO_END_CODE;
+}
+
+int RMPointer::curAction() {
+ if (_nCurSpecialPointer != 0)
+ return 0;
+
+ return _nCurPointer;
+}
+
+/**
+ * Show the cursor
+ */
+void RMPointer::showCursor() {
+ if (!CursorMan.isVisible()) {
+ CursorMan.showMouse(true);
+
+ updateCursor();
+ }
+}
+
+/**
+ * Hide the cursor
+ */
+void RMPointer::hideCursor() {
+ if (CursorMan.isVisible()) {
+ CursorMan.showMouse(false);
+ }
+}
+
+void RMPointer::doFrame() {
+ // Update the cursor animation if needed.
+ if (_nCurSpecialPointer == 0 || _nCurSpecialPointer == PTR_CUSTOM)
+ return;
+
+ RMGfxTargetBuffer buf;
+ if (_specialPointer[_nCurSpecialPointer - 1]->doFrame(&buf, false))
+ updateCursor();
+}
+
+void RMPointer::updateCursor() {
+ // Create an intermediate buffer and draw the cursor onto it
+ RMGfxTargetBuffer buf;
+ buf.create(64, 64, 16);
+ RMGfxPrimitive prim;
+
+ draw(Common::nullContext, buf, &prim);
+
+ // Get a pointer to the cursor data
+ byte *cursorData = buf;
+
+ // If in black & white mode, convert the cursor
+ if (GLOBALS._bCfgAnni30) {
+ if (!RMGfxTargetBuffer::_precalcTable) {
+ RMGfxTargetBuffer::createBWPrecalcTable();
+ }
+ uint16 *src = (uint16 *)cursorData;
+ for (int i = 0; i < 64; i++) {
+ uint16 *lineP = src;
+ for (int j = 0; j < 64; j++) {
+ lineP[j] = RMGfxTargetBuffer::_precalcTable[lineP[j] & 0x7FFF];
+ }
+ src += 64;
+ }
+ }
+
+ // Get the raw pixel data and set the cursor to it
+ Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ CursorMan.replaceCursor(cursorData, 64, 64, _cursorHotspot._x, _cursorHotspot._y, 0, 1, &pixelFormat);
+}
+
+/**
+ * Sets a new action as current
+ */
+void RMPointer::setAction(RMTonyAction action) {
+ _nCurPointer = action;
+ updateCursor();
+}
+
+/**
+ * Sets a new pointer
+ */
+void RMPointer::setSpecialPointer(PointerType ptr) {
+ _nCurSpecialPointer = ptr;
+ if (_nCurSpecialPointer && _nCurSpecialPointer != PTR_CUSTOM)
+ _specialPointer[ptr - 1]->setPattern(1);
+
+ updateCursor();
+}
+
+RMPointer::PointerType RMPointer::getSpecialPointer() {
+ return (PointerType)_nCurSpecialPointer;
+}
+
+/**
+ * Set the new custom pointer
+ */
+void RMPointer::setCustomPointer(RMGfxSourceBuffer8 *ptr) {
+ _nCurCustomPointer = ptr;
+ updateCursor();
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/game.h b/engines/tony/game.h
new file mode 100644
index 0000000000..83a1ddaea1
--- /dev/null
+++ b/engines/tony/game.h
@@ -0,0 +1,340 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_GAME_H
+#define TONY_GAME_H
+
+#include "tony/gfxcore.h"
+#include "tony/input.h"
+#include "tony/loc.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+#define INIT_GFX16_FROMRAW(dwRes, buf16) \
+ raw = new RMResRaw(dwRes); \
+ assert(raw->isValid()); \
+ assert((buf16) == NULL); \
+ (buf16) = new RMGfxSourceBuffer16(false); \
+ (buf16)->init(*raw, raw->width(), raw->height()); \
+ delete raw;
+
+#define INIT_GFX8_FROMRAW(raw, dwRes, buf8) \
+ raw = new RMResRaw(dwRes); \
+ assert(raw->isValid()); \
+ assert((buf8) == NULL); \
+ (buf8) = new RMGfxSourceBuffer8RLEByte(); \
+ (buf8)->init(*raw, raw->width(), raw->height(), true); \
+ delete raw;
+
+// X & Y dimensions of the adventure
+#define RM_SX 640
+#define RM_SY 480
+
+// X & Y dimensions of bigbuf
+#define RM_BBX (RM_SX)
+#define RM_BBY (RM_SY)
+
+// Skipping X & Y
+#define RM_SKIPY ((RM_BBY - RM_SY) / 2)
+#define RM_SKIPX 0
+
+// Tony's actions
+enum RMTonyAction {
+ TA_GOTO = 0,
+ TA_TAKE,
+ TA_USE,
+ TA_EXAMINE,
+ TA_TALK,
+ TA_PERORATE,
+
+ TA_COMBINE = 10,
+ TA_RECEIVECOMBINE,
+ TA_COMBINEGIVE,
+ TA_RECEIVECOMBINEGIVE
+};
+
+// Global Functions
+void mainEnableGUI();
+void mainDisableGUI();
+
+// Classes
+class RMPointer {
+public:
+ enum PointerType {
+ PTR_NONE = 0,
+ PTR_ARROWUP,
+ PTR_ARROWDOWN,
+ PTR_ARROWLEFT,
+ PTR_ARROWRIGHT,
+ PTR_ARROWMAP,
+ PTR_CUSTOM
+ };
+
+private:
+ RMGfxSourceBuffer8 *_pointer[16];
+ RMPoint _hotspot[16];
+ RMPoint _cursorHotspot;
+
+ RMItem *_specialPointer[16];
+
+ int _nCurPointer;
+ int _nCurSpecialPointer;
+
+ RMGfxSourceBuffer8 *_nCurCustomPointer;
+
+public:
+ /**
+ * Constructor & destructor
+ */
+ RMPointer();
+ virtual ~RMPointer();
+
+ /**
+ * Initialization
+ */
+ void init();
+
+ /**
+ * Deinitialization
+ */
+ void close();
+
+ /**
+ * Process a frame
+ */
+ void doFrame();
+
+ /**
+ * draw method
+ */
+ void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ /**
+ * Sets a new action as current
+ */
+ void setAction(RMTonyAction action);
+
+ /**
+ * Sets a new pointer
+ */
+ void setSpecialPointer(PointerType ptr);
+
+ PointerType getSpecialPointer();
+
+ /**
+ * Set the new custom pointer
+ */
+ void setCustomPointer(RMGfxSourceBuffer8 *ptr);
+
+ /**
+ * Return the current action to be applied according to the pointer
+ */
+ int curAction();
+
+ /**
+ * Update the cursor
+ */
+ void updateCursor();
+
+ /**
+ * Show the cursor
+ */
+ void showCursor();
+
+ /**
+ * Hide the cursor
+ */
+ void hideCursor();
+};
+
+class RMOptionButton: public RMGfxTaskSetPrior {
+public:
+ RMRect _rect;
+ RMGfxSourceBuffer16 *_buf;
+ bool _bActive;
+ bool _bHasGfx;
+ bool _bDoubleState;
+
+public:
+ RMOptionButton(uint32 dwRes, RMPoint pt, bool bDoubleState = false);
+ RMOptionButton(const RMRect &pt);
+ virtual ~RMOptionButton();
+
+ bool doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ void addToList(RMGfxTargetBuffer &bigBuf);
+ bool isActive();
+ void setActiveState(bool bState);
+};
+
+class RMOptionSlide : public RMGfxTaskSetPrior {
+private:
+ RMOptionButton *_pushLeft;
+ RMOptionButton *_pushRight;
+ RMGfxSourceBuffer16 *_sliderCenter;
+ RMGfxSourceBuffer16 *_sliderLeft;
+ RMGfxSourceBuffer16 *_sliderRight;
+ RMGfxSourceBuffer16 *_sliderSingle;
+ int _nSlideSize;
+ RMPoint _pos;
+ int _nValue;
+ int _nMax;
+ int _nStep;
+
+public:
+ RMOptionSlide(const RMPoint &pt, int m_nRange = 100, int m_nStartValue = 0, int slideSize = 300);
+ virtual ~RMOptionSlide();
+
+ bool doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ void addToList(RMGfxTargetBuffer &bigBuf);
+
+ int getValue();
+};
+
+class RMOptionScreen : public RMGfxWoodyBuffer {
+private:
+ RMGfxSourceBuffer16 *_menu;
+ RMGfxSourceBuffer16 *_quitConfirm;
+ RMGfxSourceBuffer16 *_hideLoadSave;
+ RMOptionButton *_buttonQuitYes;
+ RMOptionButton *_buttonQuitNo;
+ RMOptionButton *_buttonExit;
+ RMOptionButton *_buttonQuit;
+ RMOptionButton *_buttonLoad;
+ RMOptionButton *_buttonSave;
+ RMOptionButton *_buttonGameMenu;
+ RMOptionButton *_buttonGfxMenu;
+ RMOptionButton *_buttonSoundMenu;
+ RMGfxSourceBuffer8 *_saveEasy;
+ RMGfxSourceBuffer8 *_saveHard;
+ RMGfxSourceBuffer16 *_curThumb[6];
+ Common::String _curThumbName[6];
+ byte _curThumbDiff[6];
+ RMOptionButton *_buttonSave_States[6];
+ RMOptionButton *_buttonSave_ArrowLeft;
+ RMOptionButton *_buttonSave_ArrowRight;
+ RMOptionButton *_buttonGfx_Tips;
+
+ RMOptionButton *_buttonSound_DubbingOn;
+ RMOptionButton *_buttonSound_MusicOn;
+ RMOptionButton *_buttonSound_SFXOn;
+
+ RMOptionSlide *_slideTonySpeed;
+ RMOptionSlide *_slideTextSpeed;
+
+
+ int _statePos;
+ bool _bEditSaveName;
+ int _nEditPos;
+ char _editName[256];
+
+ union {
+ RMOptionButton *_buttonGame_Lock;
+ RMOptionButton *_buttonGfx_Anni30;
+ RMOptionSlide *_sliderSound_Music;
+ };
+ union {
+ RMOptionButton *_buttonGame_TimerizedText;
+ RMOptionButton *_buttonGfx_AntiAlias;
+ RMOptionSlide *_sliderSound_SFX;
+ };
+ union {
+ RMOptionButton *_buttonGame_Scrolling;
+ RMOptionButton *_buttonGfx_Sottotitoli;
+ RMOptionSlide *_sliderSound_Dubbing;
+ };
+ union {
+ RMOptionButton *_buttonGame_InterUp;
+ RMOptionButton *_buttonGfx_Trans;
+ };
+
+ int _fadeStep;
+ bool _bExit;
+ bool _bQuitConfirm;
+ int _fadeY;
+ int _fadeTime;
+ bool _bLoadMenuOnly;
+ bool _bNoLoadSave;
+ bool _bAlterGfx;
+
+ enum OptionScreenState {
+ MENUGAME,
+ MENUGFX,
+ MENUSOUND,
+ MENULOAD,
+ MENUSAVE,
+ MENUNONE
+ };
+
+ OptionScreenState _nState;
+ OptionScreenState _nLastState;
+
+public:
+ RMOptionScreen();
+ virtual ~RMOptionScreen();
+
+ void init(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result);
+ void initLoadMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result);
+ void initSaveMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result);
+ void initNoLoadSave(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result);
+ void reInit(RMGfxTargetBuffer &bigBuf);
+ bool close();
+ bool isClosing();
+
+ // Overloaded methods
+ virtual int priority();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ /**
+ * Polling for the option screen
+ */
+ void doFrame(CORO_PARAM, RMInput *m_input);
+
+ /**
+ * Retrieves a savegame's thumbnail, description, and difficulty level
+ */
+ static bool loadThumbnailFromSaveState(int numState, byte *lpDestBuf, Common::String &name, byte &diff);
+
+protected:
+
+ // Initialization and state change
+ void initState(CORO_PARAM);
+ void closeState();
+ void changeState(CORO_PARAM, OptionScreenState newState);
+
+ // Repaint the options menu
+ void refreshAll(CORO_PARAM);
+ void refreshThumbnails();
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/gfxcore.cpp b/engines/tony/gfxcore.cpp
new file mode 100644
index 0000000000..04ce01b0ed
--- /dev/null
+++ b/engines/tony/gfxcore.cpp
@@ -0,0 +1,2192 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/gfxengine.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* RMGfxTask Methods
+\****************************************************************************/
+
+RMGfxTask::RMGfxTask() {
+ _nPrior = 0;
+ _nInList = 0;
+}
+
+int RMGfxTask::priority() {
+ return _nPrior;
+}
+
+void RMGfxTask::removeThis(CORO_PARAM, bool &result) {
+ result = true;
+}
+
+/**
+ * Registration
+ */
+void RMGfxTask::Register() {
+ _nInList++;
+}
+
+void RMGfxTask::unregister() {
+ _nInList--;
+ assert(_nInList >= 0);
+}
+
+/****************************************************************************\
+* RMGfxTaskSetPrior Methods
+\****************************************************************************/
+
+void RMGfxTaskSetPrior::setPriority(int nPrior) {
+ _nPrior = nPrior;
+}
+
+
+/****************************************************************************\
+* RMGfxBuffer Methods
+\****************************************************************************/
+
+RMGfxBuffer::RMGfxBuffer() {
+ _dimx = _dimy = 0;
+ _origBuf = _buf = NULL;
+}
+
+RMGfxBuffer::~RMGfxBuffer() {
+ destroy();
+}
+
+void RMGfxBuffer::create(int dimx, int dimy, int nBpp) {
+ // Destroy the buffer it is already exists
+ if (_buf != NULL)
+ destroy();
+
+ // Copy the parameters in the private members
+ _dimx = dimx;
+ _dimy = dimy;
+
+ // Allocate a buffer
+ _origBuf = _buf = new byte[_dimx * _dimy * nBpp / 8];
+ assert(_buf != NULL);
+ Common::fill(_origBuf, _origBuf + _dimx * _dimy * nBpp / 8, 0);
+}
+
+void RMGfxBuffer::destroy() {
+ if (_origBuf != NULL && _origBuf == _buf) {
+ delete[] _origBuf;
+ _origBuf = _buf = NULL;
+ }
+}
+
+void RMGfxBuffer::offsetY(int nLines, int nBpp) {
+ _buf += nLines * getDimx() * nBpp / 8;
+}
+
+
+RMGfxBuffer::operator byte *() {
+ return _buf;
+}
+
+RMGfxBuffer::operator void *() {
+ return (void *)_buf;
+}
+
+RMGfxBuffer::RMGfxBuffer(int dimx, int dimy, int nBpp) {
+ create(dimx, dimy, nBpp);
+}
+
+int RMGfxBuffer::getDimx() {
+ return _dimx;
+}
+
+int RMGfxBuffer::getDimy() {
+ return _dimy;
+}
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer Methods
+\****************************************************************************/
+
+int RMGfxSourceBuffer::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ create(dimx, dimy, getBpp());
+ memcpy(_buf, buf, dimx * dimy * getBpp() / 8);
+
+ // Invokes the method for preparing the surface (inherited)
+ prepareImage();
+
+ return dimx * dimy * getBpp() / 8;
+}
+
+void RMGfxSourceBuffer::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ create(dimx, dimy, getBpp());
+ ds.read(_buf, dimx * dimy * getBpp() / 8);
+
+ // Invokes the method for preparing the surface (inherited)
+ prepareImage();
+}
+
+RMGfxSourceBuffer::~RMGfxSourceBuffer() {
+}
+
+void RMGfxSourceBuffer::prepareImage() {
+ // Do nothing. Can be overloaded if necessary
+}
+
+bool RMGfxSourceBuffer::clip2D(int &x1, int &y1, int &u, int &v, int &width, int &height, bool bUseSrc, RMGfxTargetBuffer *buf) {
+ int destw, desth;
+
+ destw = buf->getDimx();
+ desth = buf->getDimy();
+
+ if (!bUseSrc) {
+ u = v = 0;
+ width = _dimx;
+ height = _dimy;
+ }
+
+ if (x1 > destw - 1)
+ return false;
+
+ if (y1 > desth - 1)
+ return false;
+
+ if (x1 < 0) {
+ width += x1;
+ if (width < 0)
+ return false;
+ u -= x1;
+ x1 = 0;
+ }
+
+ if (y1 < 0) {
+ height += y1;
+ if (height < 0)
+ return false;
+ v -= y1;
+ y1 = 0;
+ }
+
+ if (x1 + width - 1 > destw - 1)
+ width = destw - x1;
+
+ if (y1 + height - 1 > desth - 1)
+ height = desth - y1;
+
+ return (width > 1 && height > 1);
+}
+
+/**
+ * Initializes a surface by resource Id
+ *
+ * @param resID Resource ID
+ * @param dimx Buffer X dimension
+ * @param dimy Buffer Y dimension
+ */
+int RMGfxSourceBuffer::init(uint32 resID, int dimx, int dimy, bool bLoadPalette) {
+ return init(RMRes(resID), dimx, dimy, bLoadPalette);
+}
+
+void RMGfxSourceBuffer::offsetY(int nLines) {
+ RMGfxBuffer::offsetY(nLines, getBpp());
+}
+
+/****************************************************************************\
+* RMGfxWoodyBuffer Methods
+\****************************************************************************/
+
+RMGfxWoodyBuffer::~RMGfxWoodyBuffer() {
+
+}
+
+void RMGfxWoodyBuffer::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Draw the OT list
+ CORO_INVOKE_0(drawOT);
+
+ // Draw itself into the target buffer
+ CORO_INVOKE_2(RMGfxSourceBuffer16::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+RMGfxWoodyBuffer::RMGfxWoodyBuffer() {
+
+}
+
+RMGfxWoodyBuffer::RMGfxWoodyBuffer(int dimx, int dimy)
+ : RMGfxBuffer(dimx, dimy, 16) {
+}
+
+/****************************************************************************\
+* RMGfxTargetBuffer Methods
+\****************************************************************************/
+
+RMGfxTargetBuffer::RMGfxTargetBuffer() {
+ _otlist = NULL;
+ _otSize = 0;
+ _trackDirtyRects = false;
+}
+
+RMGfxTargetBuffer::~RMGfxTargetBuffer() {
+ clearOT();
+}
+
+void RMGfxTargetBuffer::clearOT() {
+ OTList *cur, *n;
+
+ cur = _otlist;
+
+ while (cur != NULL) {
+ cur->_prim->_task->unregister();
+ delete cur->_prim;
+ n = cur->_next;
+ delete cur;
+ cur = n;
+ }
+
+ _otlist = NULL;
+}
+
+void RMGfxTargetBuffer::drawOT(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ OTList *cur;
+ OTList *prev;
+ OTList *next;
+ RMGfxPrimitive *myprim;
+ bool result;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->prev = NULL;
+ _ctx->cur = _otlist;
+
+ while (_ctx->cur != NULL) {
+ // Call the task Draw method, passing it a copy of the original
+ _ctx->myprim = _ctx->cur->_prim->duplicate();
+ CORO_INVOKE_2(_ctx->cur->_prim->_task->draw, *this, _ctx->myprim);
+ delete _ctx->myprim;
+
+ // Check if it's time to remove the task from the OT list
+ CORO_INVOKE_1(_ctx->cur->_prim->_task->removeThis, _ctx->result);
+ if (_ctx->result) {
+ // De-register the task
+ _ctx->cur->_prim->_task->unregister();
+
+ // Delete task, freeing the memory
+ delete _ctx->cur->_prim;
+ _ctx->next = _ctx->cur->_next;
+ delete _ctx->cur;
+
+ // If it was the first item, update the list head
+ if (_ctx->prev == NULL)
+ _otlist = _ctx->next;
+ // Otherwise update the next pinter of the previous item
+ else
+ _ctx->prev->_next = _ctx->next;
+
+ _ctx->cur = _ctx->next;
+ } else {
+ // Update the pointer to the previous item, and the current to the next
+ _ctx->prev = _ctx->cur;
+ _ctx->cur = _ctx->cur->_next;
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMGfxTargetBuffer::addPrim(RMGfxPrimitive *prim) {
+ int nPrior;
+ OTList *cur, *n;
+
+ // Warn of the OT listing
+ prim->_task->Register();
+
+ // Check the priority
+ nPrior = prim->_task->priority();
+ n = new OTList(prim);
+
+ // Empty list
+ if (_otlist == NULL) {
+ _otlist = n;
+ _otlist->_next = NULL;
+ }
+ // Inclusion in the head
+ else if (nPrior < _otlist->_prim->_task->priority()) {
+ n->_next = _otlist;
+ _otlist = n;
+ } else {
+ cur = _otlist;
+ while (cur->_next != NULL && nPrior > cur->_next->_prim->_task->priority())
+ cur = cur->_next;
+
+ n->_next = cur->_next;
+ cur->_next = n;
+ }
+}
+
+void RMGfxTargetBuffer::addDirtyRect(const Common::Rect &r) {
+ assert(r.isValidRect());
+ if (_trackDirtyRects && r.width() > 0 && r.height() > 0)
+ _currentDirtyRects.push_back(r);
+}
+
+Common::List<Common::Rect> &RMGfxTargetBuffer::getDirtyRects() {
+ // Copy rects from both the current and previous frame into the output dirty rects list
+ Common::List<Common::Rect>::iterator i;
+ _dirtyRects.clear();
+ for (i = _previousDirtyRects.begin(); i != _previousDirtyRects.end(); ++i)
+ _dirtyRects.push_back(*i);
+ for (i = _currentDirtyRects.begin(); i != _currentDirtyRects.end(); ++i)
+ _dirtyRects.push_back(*i);
+
+ mergeDirtyRects();
+ return _dirtyRects;
+}
+
+/**
+ * Move the set of dirty rects from the finished current frame into the previous frame list.
+ */
+void RMGfxTargetBuffer::clearDirtyRects() {
+ Common::List<Common::Rect>::iterator i;
+ _previousDirtyRects.clear();
+ for (i = _currentDirtyRects.begin(); i != _currentDirtyRects.end(); ++i)
+ _previousDirtyRects.push_back(*i);
+
+ _currentDirtyRects.clear();
+}
+
+/**
+ * Merges any clipping rectangles that overlap to try and reduce
+ * the total number of clip rectangles.
+ */
+void RMGfxTargetBuffer::mergeDirtyRects() {
+ if (_dirtyRects.size() <= 1)
+ return;
+
+ Common::List<Common::Rect>::iterator rOuter, rInner;
+
+ for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) {
+ rInner = rOuter;
+ while (++rInner != _dirtyRects.end()) {
+
+ if ((*rOuter).intersects(*rInner)) {
+ // these two rectangles overlap or
+ // are next to each other - merge them
+
+ (*rOuter).extend(*rInner);
+
+ // remove the inner rect from the list
+ _dirtyRects.erase(rInner);
+
+ // move back to beginning of list
+ rInner = rOuter;
+ }
+ }
+ }
+}
+
+uint16 *RMGfxTargetBuffer::_precalcTable = NULL;
+
+/**
+ * Set up the black & white precalculated mapping table. This is only
+ * called if the user selects the black & white option.
+ */
+void RMGfxTargetBuffer::createBWPrecalcTable() {
+ _precalcTable = new uint16[0x8000];
+
+ for (int i = 0; i < 0x8000; i++) {
+ int r = (i >> 10) & 0x1F;
+ int g = (i >> 5) & 0x1F;
+ int b = i & 0x1F;
+
+ int min = MIN(r, MIN(g, b));
+ int max = MAX(r, MAX(g, b));
+
+ min = (min + max) / 2;
+
+ r = CLIP(min + 8 - 8, 0, 31);
+ g = CLIP(min + 5 - 8, 0, 31);
+ b = CLIP(min + 0 - 8, 0, 31);
+
+ _precalcTable[i] = (r << 10) | (g << 5) | b;
+ }
+}
+
+/**
+ * Frees the black & white precalculated mapping table.
+ */
+void RMGfxTargetBuffer::freeBWPrecalcTable() {
+ delete[] _precalcTable;
+ _precalcTable = NULL;
+}
+
+RMGfxTargetBuffer::operator byte *() {
+ return _buf;
+}
+
+RMGfxTargetBuffer::operator void *() {
+ return (void *)_buf;
+}
+
+RMGfxTargetBuffer::operator uint16 *() {
+ // FIXME: This may not be endian safe
+ return (uint16 *)_buf;
+}
+
+/**
+ * Offseting buffer
+ */
+void RMGfxTargetBuffer::offsetY(int nLines) {
+ RMGfxBuffer::offsetY(nLines, 16);
+}
+
+void RMGfxTargetBuffer::setTrackDirtyRects(bool v) {
+ _trackDirtyRects = v;
+}
+
+bool RMGfxTargetBuffer::getTrackDirtyRects() const {
+ return _trackDirtyRects;
+}
+
+/****************************************************************************\
+* RMGfxSourceBufferPal Methods
+\****************************************************************************/
+
+RMGfxSourceBufferPal::~RMGfxSourceBufferPal() {
+
+}
+
+int RMGfxSourceBufferPal::loadPaletteWA(const byte *buf, bool bSwapped) {
+ if (bSwapped) {
+ for (int i = 0; i < (1 << getBpp()); i++) {
+ _pal[i * 3 + 0] = buf[i * 3 + 2];
+ _pal[i * 3 + 1] = buf[i * 3 + 1];
+ _pal[i * 3 + 2] = buf[i * 3 + 0];
+ }
+ } else {
+ memcpy(_pal, buf, (1 << getBpp()) * 3);
+ }
+
+ preparePalette();
+
+ return (1 << getBpp()) * 3;
+}
+
+int RMGfxSourceBufferPal::loadPalette(const byte *buf) {
+ for (int i = 0; i < 256; i++)
+ memcpy(_pal + i * 3, buf + i * 4, 3);
+
+ preparePalette();
+
+ return (1 << getBpp()) * 4;
+}
+
+void RMGfxSourceBufferPal::preparePalette() {
+ for (int i = 0; i < 256; i++) {
+ _palFinal[i] = (((int)_pal[i * 3 + 0] >> 3) << 10) |
+ (((int)_pal[i * 3 + 1] >> 3) << 5) |
+ (((int)_pal[i * 3 + 2] >> 3) << 0);
+ }
+}
+
+int RMGfxSourceBufferPal::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ // Load the RAW image
+ int read = RMGfxSourceBuffer::init(buf, dimx, dimy);
+
+ // Load the palette if necessary
+ if (bLoadPalette)
+ read += loadPaletteWA(&buf[read]);
+
+ return read;
+}
+
+void RMGfxSourceBufferPal::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ // Load the RAW image
+ RMGfxSourceBuffer::init(ds, dimx, dimy);
+
+ // Load the palette if necessary
+ if (bLoadPalette) {
+ byte *suxpal = new byte[256 * 3];
+ ds.read(suxpal, 256 * 3);
+ loadPaletteWA(suxpal);
+ delete[] suxpal;
+ }
+}
+
+int RMGfxSourceBufferPal::loadPalette(uint32 resID) {
+ return loadPalette(RMRes(resID));
+}
+
+int RMGfxSourceBufferPal::loadPaletteWA(uint32 resID, bool bSwapped) {
+ return loadPaletteWA(RMRes(resID), bSwapped);
+}
+
+/****************************************************************************\
+* RMGfxSourceBuffer4 Methods
+\****************************************************************************/
+
+void RMGfxSourceBuffer4::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+}
+
+RMGfxSourceBuffer4::RMGfxSourceBuffer4(int dimx, int dimy)
+ : RMGfxBuffer(dimx, dimy, 4) {
+ setPriority(0);
+}
+
+
+/**
+ * Returns the number of bits per pixel of the surface
+ *
+ * @returns Bit per pixel
+ */
+int RMGfxSourceBuffer4::getBpp() {
+ return 4;
+}
+
+void RMGfxSourceBuffer4::create(int dimx, int dimy) {
+ RMGfxBuffer::create(dimx, dimy, 4);
+}
+
+/****************************************************************************\
+* RMGfxSourceBuffer8 Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8::~RMGfxSourceBuffer8() {
+
+}
+
+void RMGfxSourceBuffer8::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ int width, height, u, v;
+ int bufx = bigBuf.getDimx();
+ uint16 *buf = bigBuf;
+ byte *raw = _buf;
+
+ // Destination buffer
+ RMRect dst;
+ if (prim->haveDst())
+ dst = prim->getDst();
+
+ // Clipping
+ if (prim->haveSrc()) {
+ u = prim->getSrc()._x1;
+ v = prim->getSrc()._y1;
+
+ width = prim->getSrc().width();
+ height = prim->getSrc().height();
+ }
+
+ if (!clip2D(dst._x1, dst._y1, u, v, width, height, prim->haveSrc(), &bigBuf))
+ return;
+
+ // Starting offset into the buffer
+ buf += dst._y1 * bufx + dst._x1;
+
+ // Normal step
+ if (_bTrasp0) {
+ for (int y = 0; y < height; y++) {
+ raw = _buf + (y + v) * _dimx + u;
+
+ for (int x = 0; x < width; x++) {
+ if (*raw)
+ *buf = _palFinal[*raw];
+ buf++;
+ raw++;
+ }
+
+ buf += bufx - width;
+ }
+ } else {
+ for (int y = 0; y < height; y++) {
+ raw = _buf + (y + v) * _dimx + u;
+
+ for (int x = 0; x < width; x += 2) {
+ buf[0] = _palFinal[raw[0]];
+ buf[1] = _palFinal[raw[1]];
+
+ buf += 2;
+ raw += 2;
+ }
+
+ buf += bufx - width;
+ }
+ }
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(dst._x1, dst._y1, dst._x1 + width, dst._y1 + height));
+}
+
+RMGfxSourceBuffer8::RMGfxSourceBuffer8(int dimx, int dimy)
+ : RMGfxBuffer(dimx, dimy, 8) {
+ setPriority(0);
+ _bTrasp0 = false;
+}
+
+RMGfxSourceBuffer8::RMGfxSourceBuffer8(bool bTrasp0) {
+ _bTrasp0 = bTrasp0;
+}
+
+
+/**
+ * Returns the number of bits per pixel of the surface
+ *
+ * @returns Bit per pixel
+ */
+int RMGfxSourceBuffer8::getBpp() {
+ return 8;
+}
+
+void RMGfxSourceBuffer8::create(int dimx, int dimy) {
+ RMGfxBuffer::create(dimx, dimy, 8);
+}
+
+#define GETRED(x) (((x) >> 10) & 0x1F)
+#define GETGREEN(x) (((x) >> 5) & 0x1F)
+#define GETBLUE(x) ((x) & 0x1F)
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer8AB Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8AB::~RMGfxSourceBuffer8AB() {
+
+}
+
+int RMGfxSourceBuffer8AB::calcTrasp(int fore, int back) {
+ int r = (GETRED(fore) >> 2) + (GETRED(back) >> 1);
+ int g = (GETGREEN(fore) >> 2) + (GETGREEN(back) >> 1);
+ int b = (GETBLUE(fore) >> 2) + (GETBLUE(back) >> 1);
+
+ if (r > 0x1F)
+ r = 0x1F;
+
+ if (g > 0x1F)
+ g = 0x1F;
+
+ if (b > 0x1F)
+ b = 0x1F;
+
+ return (r << 10) | (g << 5) | b;
+}
+
+
+void RMGfxSourceBuffer8AB::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ int width, height, u, v;
+ int bufx = bigBuf.getDimx();
+ uint16 *buf = bigBuf;
+ byte *raw = _buf;
+
+ // Destination buffer
+ RMRect dst;
+ if (prim->haveDst())
+ dst = prim->getDst();
+
+ // Clipping
+ if (prim->haveSrc()) {
+ u = prim->getSrc()._x1;
+ v = prim->getSrc()._y1;
+
+ width = prim->getSrc().width();
+ height = prim->getSrc().height();
+ }
+
+ if (!clip2D(dst._x1, dst._y1, u, v, width, height, prim->haveSrc(), &bigBuf))
+ return;
+
+ // Starting offset into the buffer
+ buf += dst._y1 * bufx + dst._x1;
+
+ // Passaggio normale
+ if (_bTrasp0) {
+ for (int y = 0; y < height; y++) {
+ raw = _buf + (y + v) * _dimx + u;
+
+ for (int x = 0; x < width; x++) {
+ if (*raw)
+ *buf = calcTrasp(_palFinal[*raw], *buf);
+
+ buf++;
+ raw++;
+ }
+
+ buf += bufx - width;
+ }
+ } else {
+ for (int y = 0; y < height; y++) {
+ raw = _buf + (y + v) * _dimx + u;
+
+ for (int x = 0; x < width; x += 2) {
+ buf[0] = calcTrasp(_palFinal[raw[0]], buf[0]);
+ buf[1] = calcTrasp(_palFinal[raw[1]], buf[1]);
+
+ buf += 2;
+ raw += 2;
+ }
+
+ buf += bufx - width;
+ }
+ }
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(dst._x1, dst._y1, dst._x1 + width, dst._y1 + height));
+}
+
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer8RLE Methods
+\****************************************************************************/
+
+byte RMGfxSourceBuffer8RLE::_megaRLEBuf[512 * 1024];
+
+void RMGfxSourceBuffer8RLE::setAlphaBlendColor(int color) {
+ _alphaBlendColor = color;
+}
+
+RMGfxSourceBuffer8RLE::RMGfxSourceBuffer8RLE() {
+ _alphaBlendColor = -1;
+ _bNeedRLECompress = true;
+ _buf = NULL;
+
+ _alphaR = _alphaG = _alphaB = 0;
+}
+
+RMGfxSourceBuffer8RLE::~RMGfxSourceBuffer8RLE() {
+ if (_buf != NULL) {
+ delete[] _buf;
+ _buf = NULL;
+ }
+}
+
+
+int RMGfxSourceBuffer8RLE::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ return RMGfxSourceBufferPal::init(buf, dimx, dimy, bLoadPalette);
+}
+
+void RMGfxSourceBuffer8RLE::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ if (_bNeedRLECompress) {
+ RMGfxSourceBufferPal::init(ds, dimx, dimy, bLoadPalette);
+ } else {
+ int size = ds.readSint32LE();
+ _buf = new byte[size];
+ ds.read(_buf, size);
+
+ _dimx = dimx;
+ _dimy = dimy;
+ }
+}
+
+void RMGfxSourceBuffer8RLE::preparePalette() {
+ // Invoke the parent method
+ RMGfxSourceBuffer8::preparePalette();
+
+ // Handle RGB alpha blending
+ if (_alphaBlendColor != -1) {
+ _alphaR = (_palFinal[_alphaBlendColor] >> 10) & 0x1F;
+ _alphaG = (_palFinal[_alphaBlendColor] >> 5) & 0x1F;
+ _alphaB = (_palFinal[_alphaBlendColor]) & 0x1F;
+ }
+}
+
+void RMGfxSourceBuffer8RLE::prepareImage() {
+ // Invoke the parent method
+ RMGfxSourceBuffer::prepareImage();
+
+ // Compress
+ compressRLE();
+}
+
+void RMGfxSourceBuffer8RLE::setAlreadyCompressed() {
+ _bNeedRLECompress = false;
+}
+
+void RMGfxSourceBuffer8RLE::compressRLE() {
+ byte *startline;
+ byte *cur;
+ byte curdata;
+ byte *src;
+ byte *startsrc;
+ int rep;
+
+ // Perform RLE compression for lines
+ cur = _megaRLEBuf;
+ src = _buf;
+ for (int y = 0; y < _dimy; y++) {
+ // Save the beginning of the line
+ startline = cur;
+
+ // Leave space for the length of the line
+ cur += 2;
+
+ // It starts from the empty space
+ curdata = 0;
+ rep = 0;
+ startsrc = src;
+ for (int x = 0; x < _dimx;) {
+ if ((curdata == 0 && *src == 0) || (curdata == 1 && *src == _alphaBlendColor)
+ || (curdata == 2 && (*src != _alphaBlendColor && *src != 0))) {
+ src++;
+ rep++;
+ x++;
+ } else {
+ if (curdata == 0) {
+ rleWriteTrasp(cur, rep);
+ curdata++;
+ } else if (curdata == 1) {
+ rleWriteAlphaBlend(cur, rep);
+ curdata++;
+ } else {
+ rleWriteData(cur, rep, startsrc);
+ curdata = 0;
+ }
+
+ rep = 0;
+ startsrc = src;
+ }
+ }
+
+ // Pending data?
+ if (curdata == 1) {
+ rleWriteAlphaBlend(cur, rep);
+ rleWriteData(cur, 0, NULL);
+ }
+
+ if (curdata == 2) {
+ rleWriteData(cur, rep, startsrc);
+ }
+
+ // End of line
+ rleWriteEOL(cur);
+
+ // Write the length of the line
+ WRITE_LE_UINT16(startline, (uint16)(cur - startline));
+ }
+
+ // Delete the original image
+ delete[] _buf;
+
+ // Copy the compressed image
+ int bufSize = cur - _megaRLEBuf;
+ _buf = new byte[bufSize];
+ Common::copy(_megaRLEBuf, _megaRLEBuf + bufSize, _buf);
+}
+
+void RMGfxSourceBuffer8RLE::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ byte *src;
+ uint16 *buf = bigBuf;
+ int u, v, width, height;
+
+ // Clipping
+ int x1 = prim->getDst()._x1;
+ int y1 = prim->getDst()._y1;
+ if (!clip2D(x1, y1, u, v, width, height, false, &bigBuf))
+ return;
+
+ // Go forward through the RLE lines
+ src = _buf;
+ for (int y = 0; y < v; y++)
+ src += READ_LE_UINT16(src);
+
+ // Calculate the position in the destination buffer
+ buf += y1 * bigBuf.getDimx();
+
+ // Loop
+ if (prim->isFlipped()) {
+// Eliminate horizontal clipping
+// width = m_dimx;
+// x1=prim->Dst().x1;
+
+ // Clipping
+ u = _dimx - (width + u);
+ x1 = (prim->getDst()._x1 + _dimx - 1) - u;
+
+ if (width > x1)
+ width = x1;
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1 - width, y1, x1 + 1, y1 + height));
+
+ for (int y = 0; y < height; y++) {
+ // Decompression
+ rleDecompressLineFlipped(buf + x1, src + 2, u, width);
+
+ // Next line
+ src += READ_LE_UINT16(src);
+
+ // Skip to the next line
+ buf += bigBuf.getDimx();
+ }
+ } else {
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + width, y1 + height));
+
+ for (int y = 0; y < height; y++) {
+ // Decompression
+ rleDecompressLine(buf + x1, src + 2, u, width);
+
+ // Next line
+ src += READ_LE_UINT16(src);
+
+ // Skip to the next line
+ buf += bigBuf.getDimx();
+ }
+ }
+}
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer8RLEByte Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8RLEByte::~RMGfxSourceBuffer8RLEByte() {
+}
+
+void RMGfxSourceBuffer8RLEByte::rleWriteTrasp(byte *&cur, int rep) {
+ assert(rep < 255);
+ *cur ++ = rep;
+}
+
+void RMGfxSourceBuffer8RLEByte::rleWriteAlphaBlend(byte *&cur, int rep) {
+ assert(rep < 255);
+ *cur ++ = rep;
+}
+
+void RMGfxSourceBuffer8RLEByte::rleWriteData(byte *&cur, int rep, byte *src) {
+ assert(rep < 256);
+
+ *cur ++ = rep;
+ if (rep > 0) {
+ memcpy(cur, src, rep);
+ cur += rep;
+ src += rep;
+ }
+
+ return;
+}
+
+void RMGfxSourceBuffer8RLEByte::rleWriteEOL(byte *&cur) {
+ *cur ++ = 0xFF;
+}
+
+void RMGfxSourceBuffer8RLEByte::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int n;
+
+ if (nStartSkip == 0)
+ goto RLEByteDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = *src++;
+ if (n == 0xFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst += n - nStartSkip;
+ nLength -= n - nStartSkip;
+ if (nLength > 0)
+ goto RLEByteDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = *src++;
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEByteDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // DATA
+ n = *src++;
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEByteDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEByteDoTrasp:
+ // Get the trasp of s**t
+ n = *src++;
+
+ // EOL?
+ if (n == 0xFF)
+ return;
+
+ dst += n;
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEByteDoAlpha:
+ // Alpha
+ n = *src++;
+
+RLEByteDoAlpha2:
+ if (n > nLength)
+ n = nLength;
+ for (int i = 0; i < n; i++) {
+ int r = (*dst >> 10) & 0x1F;
+ int g = (*dst >> 5) & 0x1F;
+ int b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst ++ = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+ assert(nLength > 0);
+
+//RLEByteDoCopy:
+ // Copy the stuff
+ n = *src++;
+
+RLEByteDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (int i = 0; i < n; i++)
+ *dst ++ = _palFinal[*src++];
+
+ nLength -= n;
+ if (!nLength)
+ return;
+ assert(nLength > 0);
+ }
+}
+
+void RMGfxSourceBuffer8RLEByte::rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int n;
+
+ if (nStartSkip == 0)
+ goto RLEByteFlippedDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = *src++;
+ if (n == 0xFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst -= n - nStartSkip;
+ nLength -= n - nStartSkip;
+ if (nLength > 0)
+ goto RLEByteFlippedDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = *src++;
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEByteFlippedDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // DATA
+ n = *src++;
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEByteFlippedDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEByteFlippedDoTrasp:
+ // Get the trasp of s**t
+ n = *src++;
+
+ // EOL?
+ if (n == 0xFF)
+ return;
+
+ dst -= n;
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEByteFlippedDoAlpha:
+ // Alpha
+ n = *src++;
+
+RLEByteFlippedDoAlpha2:
+ if (n > nLength)
+ n = nLength;
+ for (int i = 0; i < n; i++) {
+ int r = (*dst >> 10) & 0x1F;
+ int g = (*dst >> 5) & 0x1F;
+ int b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst-- = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+ assert(nLength > 0);
+
+//RLEByteFlippedDoCopy:
+ // Copy the data
+ n = *src++;
+
+RLEByteFlippedDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (int i = 0; i < n; i++)
+ *dst-- = _palFinal[*src++];
+
+ nLength -= n;
+ if (!nLength)
+ return;
+ assert(nLength > 0);
+ }
+}
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer8RLEWord Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8RLEWord::~RMGfxSourceBuffer8RLEWord() {
+
+}
+
+void RMGfxSourceBuffer8RLEWord::rleWriteTrasp(byte *&cur, int rep) {
+ WRITE_LE_UINT16(cur, rep);
+ cur += 2;
+}
+
+void RMGfxSourceBuffer8RLEWord::rleWriteAlphaBlend(byte *&cur, int rep) {
+ WRITE_LE_UINT16(cur, rep);
+ cur += 2;
+}
+
+void RMGfxSourceBuffer8RLEWord::rleWriteData(byte *&cur, int rep, byte *src) {
+ WRITE_LE_UINT16(cur, rep);
+ cur += 2;
+
+ if (rep > 0) {
+ memcpy(cur, src, rep);
+ cur += rep;
+ src += rep;
+ }
+}
+
+void RMGfxSourceBuffer8RLEWord::rleWriteEOL(byte *&cur) {
+ *cur ++ = 0xFF;
+ *cur ++ = 0xFF;
+}
+
+void RMGfxSourceBuffer8RLEWord::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int n;
+
+ if (nStartSkip == 0)
+ goto RLEWordDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n == 0xFFFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst += n - nStartSkip;
+ nLength -= n - nStartSkip;
+
+ if (nLength > 0)
+ goto RLEWordDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEWordDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ // DATA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEWordDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEWordDoTrasp:
+ // Get the trasp of s**t
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ // EOL?
+ if (n == 0xFFFF)
+ return;
+
+ dst += n;
+
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEWordDoAlpha:
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordDoAlpha2:
+
+ if (n > nLength)
+ n = nLength;
+
+ for (int i = 0; i < n; i++) {
+ int r = (*dst >> 10) & 0x1F;
+ int g = (*dst >> 5) & 0x1F;
+ int b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst++ = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+
+//RLEWordDoCopy:
+ // Copy the data
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (int i = 0; i < n; i++)
+ *dst++ = _palFinal[*src++];
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+
+ }
+}
+
+void RMGfxSourceBuffer8RLEWord::rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int n;
+
+ if (nStartSkip == 0)
+ goto RLEWordFlippedDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n == 0xFFFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst -= n - nStartSkip;
+ nLength -= n - nStartSkip;
+
+ if (nLength > 0)
+ goto RLEWordFlippedDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEWordFlippedDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ // DATA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEWordFlippedDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEWordFlippedDoTrasp:
+ // Get the trasp of s**t
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ // EOL?
+ if (n == 0xFFFF)
+ return;
+
+ dst -= n;
+
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEWordFlippedDoAlpha:
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordFlippedDoAlpha2:
+
+ if (n > nLength)
+ n = nLength;
+
+ for (int i = 0; i < n; i++) {
+ int r = (*dst >> 10) & 0x1F;
+ int g = (*dst >> 5) & 0x1F;
+ int b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst-- = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+
+//RLEWordFlippedDoCopy:
+ // Copy the data
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordFlippedDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (int i = 0; i < n; i++)
+ *dst-- = _palFinal[*src++];
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+ }
+}
+
+/****************************************************************************\
+* Methods for RMGfxSourceBuffer8RLEWord
+\****************************************************************************/
+
+RMGfxSourceBuffer8RLEWordAB::~RMGfxSourceBuffer8RLEWordAB() {
+
+}
+
+void RMGfxSourceBuffer8RLEWordAB::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int n;
+
+ if (!GLOBALS._bCfgTransparence) {
+ RMGfxSourceBuffer8RLEWord::rleDecompressLine(dst, src, nStartSkip, nLength);
+ return;
+ }
+
+ if (nStartSkip == 0)
+ goto RLEWordDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n == 0xFFFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst += n - nStartSkip;
+ nLength -= n - nStartSkip;
+
+ if (nLength > 0)
+ goto RLEWordDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEWordDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ // DATA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEWordDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEWordDoTrasp:
+ // Get the trasp of s**t
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ // EOL?
+ if (n == 0xFFFF)
+ return;
+
+ dst += n;
+
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEWordDoAlpha:
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordDoAlpha2:
+
+ if (n > nLength)
+ n = nLength;
+
+ // @@@ SHOULD NOT BE THERE !!!!!
+ for (int i = 0; i < n; i++) {
+ int r = (*dst >> 10) & 0x1F;
+ int g = (*dst >> 5) & 0x1F;
+ int b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst++ = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+
+//RLEWordDoCopy:
+ // Copy the data
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (int i = 0; i < n; i++) {
+ int r = (*dst >> 10) & 0x1F;
+ int g = (*dst >> 5) & 0x1F;
+ int b = *dst & 0x1F;
+
+ int r2 = (_palFinal[*src] >> 10) & 0x1F;
+ int g2 = (_palFinal[*src] >> 5) & 0x1F;
+ int b2 = _palFinal[*src] & 0x1F;
+
+ r = (r >> 1) + (r2 >> 1);
+ g = (g >> 1) + (g2 >> 1);
+ b = (b >> 1) + (b2 >> 1);
+
+ *dst ++ = (r << 10) | (g << 5) | b;
+ src++;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+ }
+}
+
+/****************************************************************************\
+* Methods for RMGfxSourceBuffer8AA
+\****************************************************************************/
+
+byte RMGfxSourceBuffer8AA::_megaAABuf[256 * 1024];
+byte RMGfxSourceBuffer8AA::_megaAABuf2[64 * 1024];
+
+void RMGfxSourceBuffer8AA::prepareImage() {
+ // Invoke the parent method
+ RMGfxSourceBuffer::prepareImage();
+
+ // Prepare the buffer for anti-aliasing
+ calculateAA();
+}
+
+void RMGfxSourceBuffer8AA::calculateAA() {
+ byte *src, *srcaa;
+
+ // First pass: fill the edges
+ Common::fill(_megaAABuf, _megaAABuf + _dimx * _dimy, 0);
+
+ src = _buf;
+ srcaa = _megaAABuf;
+ for (int y = 0; y < _dimy; y++) {
+ for (int x = 0; x < _dimx; x++) {
+ if (*src == 0) {
+ if ((y > 0 && src[-_dimx] != 0) ||
+ (y < _dimy - 1 && src[_dimx] != 0) ||
+ (x > 0 && src[-1] != 0) ||
+ (x < _dimx - 1 && src[1] != 0))
+ *srcaa = 1;
+ }
+
+ src++;
+ srcaa++;
+ }
+ }
+
+ src = _buf;
+ srcaa = _megaAABuf;
+ for (int y = 0; y < _dimy; y++) {
+ for (int x = 0; x < _dimx; x++) {
+ if (*src != 0) {
+ if ((y > 0 && srcaa[-_dimx] == 1) ||
+ (y < _dimy - 1 && srcaa[_dimx] == 1) ||
+ (x > 0 && srcaa[-1] == 1) ||
+ (x < _dimx - 1 && srcaa[1] == 1))
+ *srcaa = 2;
+ }
+
+ src++;
+ srcaa++;
+ }
+ }
+
+ if (_aabuf != NULL)
+ delete[] _aabuf;
+
+ _aabuf = new byte[_dimx * _dimy];
+ memcpy(_aabuf, _megaAABuf, _dimx * _dimy);
+}
+
+RMGfxSourceBuffer8AA::RMGfxSourceBuffer8AA() : RMGfxSourceBuffer8() {
+ _aabuf = NULL;
+}
+
+RMGfxSourceBuffer8AA::~RMGfxSourceBuffer8AA() {
+ if (_aabuf != NULL)
+ delete[] _aabuf;
+}
+
+void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ byte *src;
+ uint16 *mybuf;
+ uint16 *buf;
+ int u, v, width, height;
+
+ // Clip the sprite
+ int x1 = prim->getDst()._x1;
+ int y1 = prim->getDst()._y1;
+ if (!clip2D(x1, y1, u, v, width, height, false, &bigBuf))
+ return;
+
+ // Go forward through the RLE lines
+ src = _buf;
+ for (int y = 0; y < v; y++)
+ src += READ_LE_UINT16(src);
+
+ // Eliminate horizontal clipping
+
+ if (prim->isFlipped()) {
+ u = _dimx - (width + u);
+ x1 = (prim->getDst()._x1 + _dimx - 1) - u;
+
+ if (width > x1)
+ width = x1;
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1 - width, y1, x1 + 1, y1 + height));
+ } else {
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + width, y1 + height));
+ }
+
+// width = _dimx;
+// x1 = prim->Dst().x1;
+
+
+ // Position into the destination buffer
+ buf = bigBuf;
+ buf += y1 * bigBuf.getDimx();
+
+ int step;
+ if (prim->isFlipped())
+ step = -1;
+ else
+ step = 1;
+
+ // Loop
+ buf += bigBuf.getDimx(); // Skip the first line
+ for (int y = 1; y < height - 1; y++) {
+ // if (prim->IsFlipped())
+ // mybuf=&buf[x1+m_dimx-1];
+ // else
+ mybuf = &buf[x1];
+
+ for (int x = 0; x < width; x++, mybuf += step) {
+ if (_aabuf[(y + v) * _dimx + x + u] == 2 && x != 0 && x != width - 1) {
+ int r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]);
+ int g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]);
+ int b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]);
+
+ r += GETRED(mybuf[0]);
+ g += GETGREEN(mybuf[0]);
+ b += GETBLUE(mybuf[0]);
+
+ r /= 5;
+ g /= 5;
+ b /= 5;
+
+ if (r > 31)
+ r = 31;
+ if (g > 31)
+ g = 31;
+ if (b > 31)
+ b = 31;
+
+ mybuf[0] = (r << 10) | (g << 5) | b;
+ }
+ }
+
+ // Skip to the next line
+ buf += bigBuf.getDimx();
+ }
+
+ // Position into the destination buffer
+ buf = bigBuf;
+ buf += y1 * bigBuf.getDimx();
+
+ // Looppone
+ buf += bigBuf.getDimx();
+ for (int y = 1; y < height - 1; y++) {
+ // if (prim->IsFlipped())
+ // mybuf=&buf[x1+m_dimx-1];
+ // else
+ mybuf = &buf[x1];
+
+ for (int x = 0; x < width; x++, mybuf += step) {
+ if (_aabuf[(y + v) * _dimx + x + u] == 1 && x != 0 && x != width - 1) {
+ int r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]);
+ int g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]);
+ int b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]);
+
+ r += GETRED(mybuf[0]) * 2;
+ g += GETGREEN(mybuf[0]) * 2;
+ b += GETBLUE(mybuf[0]) * 2;
+
+ r /= 6;
+ g /= 6;
+ b /= 6;
+
+ if (r > 31)
+ r = 31;
+ if (g > 31)
+ g = 31;
+ if (b > 31)
+ b = 31;
+
+ mybuf[0] = (r << 10) | (g << 5) | b;
+ }
+ }
+
+ // Skip to the next line
+ buf += bigBuf.getDimx();
+ }
+}
+
+
+
+void RMGfxSourceBuffer8AA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(RMGfxSourceBuffer8::draw, bigBuf, prim);
+ drawAA(bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+/****************************************************************************\
+* RMGfxSourceBuffer8RLEAA Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8RLEByteAA::~RMGfxSourceBuffer8RLEByteAA() {
+}
+
+void RMGfxSourceBuffer8RLEByteAA::prepareImage() {
+ RMGfxSourceBuffer::prepareImage();
+ calculateAA();
+ compressRLE();
+}
+
+void RMGfxSourceBuffer8RLEByteAA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(RMGfxSourceBuffer8RLE::draw, bigBuf, prim);
+ if (GLOBALS._bCfgAntiAlias)
+ drawAA(bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+int RMGfxSourceBuffer8RLEByteAA::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ return RMGfxSourceBuffer8RLE::init(buf, dimx, dimy, bLoadPalette);
+}
+
+void RMGfxSourceBuffer8RLEByteAA::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ RMGfxSourceBuffer8RLE::init(ds, dimx, dimy, bLoadPalette);
+
+ if (!_bNeedRLECompress) {
+ // Load the anti-aliasing mask
+ _aabuf = new byte[dimx * dimy];
+ ds.read(_aabuf, dimx * dimy);
+ }
+}
+
+RMGfxSourceBuffer8RLEWordAA::~RMGfxSourceBuffer8RLEWordAA() {
+}
+
+void RMGfxSourceBuffer8RLEWordAA::prepareImage() {
+ RMGfxSourceBuffer::prepareImage();
+ calculateAA();
+ compressRLE();
+}
+
+void RMGfxSourceBuffer8RLEWordAA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(RMGfxSourceBuffer8RLE::draw, bigBuf, prim);
+ if (GLOBALS._bCfgAntiAlias)
+ drawAA(bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+int RMGfxSourceBuffer8RLEWordAA::init(byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ return RMGfxSourceBuffer8RLE::init(buf, dimx, dimy, bLoadPalette);
+}
+
+void RMGfxSourceBuffer8RLEWordAA::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ RMGfxSourceBuffer8RLE::init(ds, dimx, dimy, bLoadPalette);
+
+ if (!_bNeedRLECompress) {
+ // Load the anti-aliasing mask
+ _aabuf = new byte[dimx * dimy];
+ ds.read(_aabuf, dimx * dimy);
+ }
+}
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer16 Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer16::RMGfxSourceBuffer16(bool bTrasp0) {
+ _bTrasp0 = bTrasp0;
+}
+
+RMGfxSourceBuffer16::~RMGfxSourceBuffer16() {
+}
+
+void RMGfxSourceBuffer16::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ uint16 *buf = bigBuf;
+ uint16 *raw = (uint16 *)_buf;
+
+ int dimx = _dimx;
+ int dimy = _dimy;
+ int u = 0;
+ int v = 0;
+ int x1 = 0;
+ int y1 = 0;
+
+ if (prim->haveSrc()) {
+ u = prim->getSrc()._x1;
+ v = prim->getSrc()._y1;
+ dimx = prim->getSrc().width();
+ dimy = prim->getSrc().height();
+ }
+
+ if (prim->haveDst()) {
+ x1 = prim->getDst()._x1;
+ y1 = prim->getDst()._y1;
+ }
+
+ if (!clip2D(x1, y1, u, v, dimx, dimy, true, &bigBuf))
+ return;
+
+ raw += v * _dimx + u;
+ buf += y1 * bigBuf.getDimx() + x1;
+
+ if (_bTrasp0) {
+ for (int y = 0; y < dimy; y++) {
+ for (int x = 0; x < dimx;) {
+ while (x < dimx && raw[x] == 0)
+ x++;
+
+ while (x < dimx && raw[x] != 0) {
+ buf[x] = raw[x];
+ x++;
+ }
+ }
+
+ raw += _dimx;
+ buf += bigBuf.getDimx();
+ }
+ } else {
+ for (int y = 0; y < dimy; y++) {
+ Common::copy(raw, raw + dimx, buf);
+ buf += bigBuf.getDimx();
+ raw += _dimx;
+ }
+ }
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + dimx, y1 + dimy));
+}
+
+void RMGfxSourceBuffer16::prepareImage() {
+ // Color space conversion if necessary!
+ uint16 *buf = (uint16 *)_buf;
+
+ for (int i = 0; i < _dimx * _dimy; i++)
+ WRITE_LE_UINT16(&buf[i], FROM_LE_16(buf[i]) & 0x7FFF);
+}
+
+RMGfxSourceBuffer16::RMGfxSourceBuffer16(int dimx, int dimy)
+ : RMGfxBuffer(dimx, dimy, 16) {
+ setPriority(0);
+ _bTrasp0 = false;
+}
+
+/**
+ * Returns the number of bits per pixel of the surface
+ *
+ * @returns Bit per pixel
+ */
+int RMGfxSourceBuffer16::getBpp() {
+ return 16;
+}
+
+void RMGfxSourceBuffer16::create(int dimx, int dimy) {
+ RMGfxBuffer::create(dimx, dimy, 16);
+}
+
+/****************************************************************************\
+* RMGfxBox Methods
+\****************************************************************************/
+
+void RMGfxBox::removeThis(CORO_PARAM, bool &result) {
+ result = true;
+}
+
+void RMGfxBox::setColor(byte r, byte g, byte b) {
+ r >>= 3;
+ g >>= 3;
+ b >>= 3;
+ _wFillColor = (r << 10) | (g << 5) | b;
+}
+
+void RMGfxBox::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ uint16 *buf = bigBuf;
+ RMRect rcDst;
+
+ // It takes the destination rectangle
+ rcDst = prim->getDst();
+ buf += rcDst._y1 * bigBuf.getDimx() + rcDst._x1;
+
+ // Loop through the pixels
+ for (int j = 0; j < rcDst.height(); j++) {
+ for (int i = 0; i < rcDst.width(); i++)
+ *buf++ = _wFillColor;
+
+ buf += bigBuf.getDimx() - rcDst.width();
+ }
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(rcDst);
+}
+
+
+/****************************************************************************\
+* RMGfxClearTask Methods
+\****************************************************************************/
+
+int RMGfxClearTask::priority() {
+ // Maximum priority (must be done first)
+ return 1;
+}
+
+void RMGfxClearTask::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *) {
+ // Clean the target buffer
+ Common::fill((byte *)bigBuf, (byte *)bigBuf + (bigBuf.getDimx() * bigBuf.getDimy() * 2), 0x0);
+ bigBuf.addDirtyRect(Common::Rect(bigBuf.getDimx(), bigBuf.getDimy()));
+}
+
+void RMGfxClearTask::removeThis(CORO_PARAM, bool &result) {
+ // The task is fine to be removed
+ result = true;
+}
+
+/****************************************************************************\
+* RMGfxPrimitive Methods
+\****************************************************************************/
+
+RMGfxPrimitive::RMGfxPrimitive() {
+ _bFlag = 0;
+ _task = NULL;
+ _src.setEmpty();
+ _dst.setEmpty();
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task) {
+ _task = task;
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMRect &dst) {
+ _task = task;
+ _src = src;
+ _dst = dst;
+ _bFlag = 0;
+ _bStretch = (src.width() != dst.width() || src.height() != dst.height());
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMRect &dst) {
+ _task = task;
+ _src.topLeft() = src;
+ _dst = dst;
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMPoint &dst) {
+ _task = task;
+ _src.topLeft() = src;
+ _dst.topLeft() = dst;
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMPoint &dst) {
+ _task = task;
+ _src = src;
+ _dst.topLeft() = dst;
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &dst) {
+ _task = task;
+ _dst = dst;
+ _src.setEmpty();
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &dst) {
+ _task = task;
+ _dst.topLeft() = dst;
+ _src.setEmpty();
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::~RMGfxPrimitive() {
+}
+
+void RMGfxPrimitive::setFlag(byte bFlag) {
+ _bFlag = bFlag;
+}
+
+void RMGfxPrimitive::setTask(RMGfxTask *task) {
+ _task = task;
+}
+
+void RMGfxPrimitive::setSrc(const RMRect &src) {
+ _src = src;
+}
+
+void RMGfxPrimitive::setSrc(const RMPoint &src) {
+ _src.topLeft() = src;
+}
+
+void RMGfxPrimitive::setDst(const RMRect &dst) {
+ _dst = dst;
+}
+
+void RMGfxPrimitive::setDst(const RMPoint &dst) {
+ _dst.topLeft() = dst;
+}
+
+void RMGfxPrimitive::setStretch(bool bStretch) {
+ _bStretch = bStretch;
+}
+
+bool RMGfxPrimitive::haveDst() {
+ return !_dst.isEmpty();
+}
+
+RMRect &RMGfxPrimitive::getDst() {
+ return _dst;
+}
+
+bool RMGfxPrimitive::haveSrc() {
+ return !_src.isEmpty();
+}
+
+RMRect &RMGfxPrimitive::getSrc() {
+ return _src;
+}
+
+/**
+ * Flags
+ */
+bool RMGfxPrimitive::isFlipped() {
+ return _bFlag & 1;
+}
+
+/**
+ * Duplicate
+ */
+RMGfxPrimitive *RMGfxPrimitive::duplicate() {
+ return new RMGfxPrimitive(*this);
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/gfxcore.h b/engines/tony/gfxcore.h
new file mode 100644
index 0000000000..f0deed83ee
--- /dev/null
+++ b/engines/tony/gfxcore.h
@@ -0,0 +1,516 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_GFXCORE_H
+#define TONY_GFXCORE_H
+
+#include "common/system.h"
+#include "common/coroutines.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* Class prototype
+\****************************************************************************/
+
+// Class Name Family Treee Abstract?
+class RMGfxTask; // Yes
+class RMGfxTaskSetPrior; // Task Yes
+class RMGfxBuffer; //
+class RMGfxSourceBuffer; // TaskP+[Buffer] Yes
+class RMGfxTargetBuffer; // [Buffer]
+class RMGfxSourceBufferPal; // Source Yes
+class RMGfxSourceBuffer4; // SourcePal
+class RMGfxSourceBuffer8; // SourcePal
+class RMGfxSourceBuffer16; // Source
+class RMGfxWoodyBuffer; // Source16+Target
+class RMGfxClearTask; // Task
+
+
+/**
+ * Graphics buffer
+ */
+class RMGfxBuffer {
+protected:
+ int _dimx, _dimy;
+ byte *_buf;
+ byte *_origBuf;
+
+public:
+ RMGfxBuffer();
+ RMGfxBuffer(int dimx, int dimy, int nBpp);
+ virtual ~RMGfxBuffer();
+
+ // Attributes
+ int getDimx();
+ int getDimy();
+
+ // Creation
+ virtual void create(int dimx, int dimy, int nBpp);
+ virtual void destroy();
+
+ // These are valid only if the buffer is locked
+ operator byte *();
+ operator void *();
+
+ // Getting the offset for a given Y position
+ void offsetY(int nLines, int nBpp);
+};
+
+/**
+ * Graphics primitive
+ */
+class RMGfxPrimitive {
+public:
+ RMGfxTask *_task;
+
+protected:
+ RMRect _src;
+ RMRect _dst;
+
+ bool _bStretch;
+ byte _bFlag;
+
+public:
+ RMGfxPrimitive();
+ RMGfxPrimitive(RMGfxTask *task);
+ RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMRect &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMRect &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMPoint &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMPoint &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMRect &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMPoint &dst);
+ virtual ~RMGfxPrimitive();
+ void setFlag(byte bFlag);
+ void setTask(RMGfxTask *task);
+ void setSrc(const RMRect &src);
+ void setSrc(const RMPoint &src);
+ void setDst(const RMRect &dst);
+ void setDst(const RMPoint &dst);
+ void setStretch(bool bStretch);
+ bool haveDst();
+ RMRect &getDst();
+ bool haveSrc();
+ RMRect &getSrc();
+
+ // Flags
+ bool isFlipped();
+
+ // Duplicate
+ virtual RMGfxPrimitive *duplicate();
+};
+
+
+/**
+ * Graphic drawing task
+ */
+class RMGfxTask {
+protected:
+ int _nPrior;
+ int _nInList;
+
+public:
+ // Standard constructor
+ RMGfxTask();
+ virtual ~RMGfxTask() { }
+
+ virtual int priority();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) = 0;
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Registration
+ virtual void Register();
+ virtual void unregister();
+};
+
+
+/**
+ * Graphic drawing with priority
+ */
+class RMGfxTaskSetPrior : public RMGfxTask {
+public:
+ virtual ~RMGfxTaskSetPrior() { }
+ void setPriority(int nPrior);
+};
+
+
+/**
+ * Task that cleans the destination buffer
+ */
+class RMGfxClearTask : public RMGfxTask {
+public:
+ virtual ~RMGfxClearTask() { }
+
+ int priority();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ virtual void removeThis(CORO_PARAM, bool &result);
+};
+
+
+/**
+ * Task that draws a colored box
+ */
+class RMGfxBox : public RMGfxTaskSetPrior {
+protected:
+ uint16 _wFillColor;
+
+public:
+ virtual ~RMGfxBox() { }
+
+ void setColor(byte r, byte g, byte b);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ virtual void removeThis(CORO_PARAM, bool &result);
+};
+
+
+/**
+ * Buffer source for the design, which is a task. This is an abstract base.
+ */
+class RMGfxSourceBuffer : public virtual RMGfxBuffer, public RMGfxTaskSetPrior {
+public:
+ // Load the data for the surface
+ virtual int init(uint32 resID, int dimx, int dimy, bool bLoadPalette = false);
+ virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+
+ virtual ~RMGfxSourceBuffer();
+
+protected:
+ virtual void prepareImage();
+ bool clip2D(int &x1, int &y1, int &u, int &v, int &width, int &height, bool bUseSrc, RMGfxTargetBuffer *buf);
+ void offsetY(int nLines);
+
+public:
+ virtual int getBpp() = 0;
+};
+
+
+/**
+ * 16-bit color source
+ */
+class RMGfxSourceBuffer16 : public RMGfxSourceBuffer {
+protected:
+ virtual void prepareImage();
+ bool _bTrasp0;
+
+public:
+ RMGfxSourceBuffer16(bool bUseTrasp = false);
+ RMGfxSourceBuffer16(int dimx, int dimy);
+ virtual ~RMGfxSourceBuffer16();
+
+ // Initialization
+ void create(int dimx, int dimy);
+
+ int getBpp();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Buffer source with palette
+ */
+class RMGfxSourceBufferPal : public RMGfxSourceBuffer {
+protected:
+ // The size of the palette is (1 << Bpp()) * 4
+ byte _pal[256 * 3];
+ uint16 _palFinal[256];
+
+ // Post process to prepare the palette for drawing
+ virtual void preparePalette();
+
+public:
+ virtual ~RMGfxSourceBufferPal();
+
+ virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+
+ int loadPaletteWA(uint32 resID, bool bSwapped = false);
+ int loadPaletteWA(const byte *buf, bool bSwapped = false);
+ int loadPalette(uint32 resID);
+ int loadPalette(const byte *buf);
+};
+
+
+/**
+ * Buffer source with a 256 color palette
+ */
+class RMGfxSourceBuffer8 : public RMGfxSourceBufferPal {
+protected:
+ bool _bTrasp0;
+
+public:
+ RMGfxSourceBuffer8(bool bTrasp0 = true);
+ RMGfxSourceBuffer8(int dimx, int dimy);
+ virtual ~RMGfxSourceBuffer8();
+
+ // Initialization
+ void create(int dimx, int dimy);
+
+ int getBpp();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Buffer source with a 256 color palette, and alpha blending
+ */
+class RMGfxSourceBuffer8AB : public RMGfxSourceBuffer8 {
+protected:
+ int calcTrasp(int f, int b);
+
+public:
+ virtual ~RMGfxSourceBuffer8AB();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Buffer source with a 256 color palette, RLE compressed
+ */
+
+class RMGfxSourceBuffer8RLE : public virtual RMGfxSourceBuffer8 {
+protected:
+ int _alphaBlendColor;
+ int _alphaR, _alphaB, _alphaG;
+ bool _bNeedRLECompress;
+
+protected:
+ static byte _megaRLEBuf[];
+
+ virtual void rleWriteTrasp(byte *&cur, int rep) = 0;
+ virtual void rleWriteData(byte *&cur, int rep, byte *src) = 0;
+ virtual void rleWriteEOL(byte *&cur) = 0;
+ virtual void rleWriteAlphaBlend(byte *&cur, int rep) = 0;
+ virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0;
+ virtual void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0;
+
+ // Perform image compression in RLE
+ void compressRLE();
+
+protected:
+ // Overriding initialization methods
+ virtual void prepareImage();
+ virtual void preparePalette();
+
+public:
+ RMGfxSourceBuffer8RLE();
+ virtual ~RMGfxSourceBuffer8RLE();
+
+ // Overload of the initialization method
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+ virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+
+ // Draw image with RLE decompression
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Sets the color that will be alpha blended
+ void setAlphaBlendColor(int color);
+
+ // Warn if the data is already compressed
+ void setAlreadyCompressed();
+};
+
+class RMGfxSourceBuffer8RLEByte : public RMGfxSourceBuffer8RLE {
+protected:
+ void rleWriteTrasp(byte * &cur, int rep);
+ void rleWriteAlphaBlend(byte * &cur, int rep);
+ void rleWriteData(byte * &cur, int rep, byte *src);
+ void rleWriteEOL(byte * &cur);
+ void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength);
+ void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength);
+
+public:
+ virtual ~RMGfxSourceBuffer8RLEByte();
+};
+
+class RMGfxSourceBuffer8RLEWord : public RMGfxSourceBuffer8RLE {
+protected:
+ void rleWriteTrasp(byte * &cur, int rep);
+ void rleWriteAlphaBlend(byte * &cur, int rep);
+ void rleWriteData(byte * &cur, int rep, byte *src);
+ void rleWriteEOL(byte * &cur);
+ virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength);
+ virtual void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength);
+
+public:
+ virtual ~RMGfxSourceBuffer8RLEWord();
+};
+
+class RMGfxSourceBuffer8RLEWordAB : public RMGfxSourceBuffer8RLEWord {
+protected:
+ virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength);
+
+public:
+ virtual ~RMGfxSourceBuffer8RLEWordAB();
+};
+
+
+/**
+ * Buffer source with a 256 color palette, with anti-aliasing
+ */
+class RMGfxSourceBuffer8AA : public virtual RMGfxSourceBuffer8 {
+protected:
+ static byte _megaAABuf[];
+ static byte _megaAABuf2[];
+ byte *_aabuf;
+
+ // Calculate the buffer for the anti-aliasing
+ void calculateAA();
+
+ // Draw the AA
+ void drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+protected:
+ void prepareImage();
+
+public:
+ RMGfxSourceBuffer8AA();
+ virtual ~RMGfxSourceBuffer8AA();
+
+ // Draw with anti-aliasing
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+class RMGfxSourceBuffer8RLEByteAA : public RMGfxSourceBuffer8RLEByte, public RMGfxSourceBuffer8AA {
+protected:
+ void prepareImage();
+
+public:
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Overloaded initialization methods
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+ virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+
+ virtual ~RMGfxSourceBuffer8RLEByteAA();
+};
+
+class RMGfxSourceBuffer8RLEWordAA : public RMGfxSourceBuffer8RLEWord, public RMGfxSourceBuffer8AA {
+protected:
+ void prepareImage();
+
+public:
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Overloaded initialization methods
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+ virtual int init(byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+
+ virtual ~RMGfxSourceBuffer8RLEWordAA();
+};
+
+
+/**
+ * Source buffer with 16 colors
+ */
+class RMGfxSourceBuffer4 : public RMGfxSourceBufferPal {
+public:
+ RMGfxSourceBuffer4();
+ RMGfxSourceBuffer4(int dimx, int dimy);
+
+ // Initialization
+ void create(int dimx, int dimy);
+
+ int getBpp();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Destination buffer which manages its own internal list of tasks
+ */
+class RMGfxTargetBuffer : public virtual RMGfxBuffer {
+private:
+ struct OTList {
+ RMGfxPrimitive *_prim;
+ OTList *_next;
+
+ OTList();
+ OTList(RMGfxPrimitive *pr) {
+ _prim = pr;
+ }
+ };
+
+ bool _trackDirtyRects;
+ Common::List<Common::Rect> _currentDirtyRects, _previousDirtyRects, _dirtyRects;
+
+ void mergeDirtyRects();
+
+private:
+// OSystem::MutexRef csModifyingOT;
+
+protected:
+ OTList *_otlist;
+ int _otSize;
+
+public:
+ RMGfxTargetBuffer();
+ virtual ~RMGfxTargetBuffer();
+
+ static uint16 *_precalcTable;
+ static void createBWPrecalcTable();
+ static void freeBWPrecalcTable();
+
+ // management of the OT list
+ void clearOT();
+ void drawOT(CORO_PARAM);
+ void addPrim(RMGfxPrimitive *prim); // The pointer must be delted
+
+ operator byte *();
+ operator void *();
+ operator uint16 *();
+
+ // Offseting buffer
+ void offsetY(int nLines);
+
+ // Dirty rect methods
+ void addDirtyRect(const Common::Rect &r);
+ Common::List<Common::Rect> &getDirtyRects();
+ void clearDirtyRects();
+ void setTrackDirtyRects(bool v);
+ bool getTrackDirtyRects() const;
+};
+
+
+/**
+ * Ring buffer, which is both source and by destination
+ */
+class RMGfxWoodyBuffer: public RMGfxSourceBuffer16, public RMGfxTargetBuffer {
+public:
+ RMGfxWoodyBuffer();
+ RMGfxWoodyBuffer(int dimx, int dimy);
+ virtual ~RMGfxWoodyBuffer();
+
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/gfxengine.cpp b/engines/tony/gfxengine.cpp
new file mode 100644
index 0000000000..59fb024622
--- /dev/null
+++ b/engines/tony/gfxengine.cpp
@@ -0,0 +1,843 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/savefile.h"
+#include "tony/mpal/lzo.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/custom.h"
+#include "tony/gfxengine.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+
+/****************************************************************************\
+* RMGfxEngine Methods
+\****************************************************************************/
+
+void exitAllIdles(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ int nCurLoc = *(const int *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Closes idle
+ GLOBALS._bSkipSfxNoLoop = true;
+
+ CORO_INVOKE_2(mpalEndIdlePoll, nCurLoc, NULL);
+
+ GLOBALS._bIdleExited = true;
+ GLOBALS._bSkipSfxNoLoop = false;
+
+ CORO_END_CODE;
+}
+
+RMGfxEngine::RMGfxEngine() {
+ // Create big buffer where the frame will be rendered
+ _bigBuf.create(RM_BBX, RM_BBY, 16);
+ _bigBuf.offsetY(RM_SKIPY);
+ _bigBuf.setTrackDirtyRects(true);
+
+ _nCurLoc = 0;
+ _curAction = TA_GOTO;
+ _curActionObj = 0;
+ _nWipeType = 0;
+ _hWipeEvent = 0;
+ _nWipeStep = 0;
+ _bMustEnterMenu = false;
+ _bWiping = false;
+ _bGUIOption = false;
+ _bGUIInterface = false;
+ _bGUIInventory = false;
+ _bAlwaysDrawMouse = false;
+ _bOption = false;
+ _bLocationLoaded = false;
+ _bInput = false;
+}
+
+RMGfxEngine::~RMGfxEngine() {
+ // Close the buffer
+ _bigBuf.destroy();
+}
+
+void RMGfxEngine::openOptionScreen(CORO_PARAM, int type) {
+ CORO_BEGIN_CONTEXT;
+ bool bRes;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bRes = false;
+
+ if (type == 0)
+ CORO_INVOKE_2(_opt.init, _bigBuf, _ctx->bRes);
+ else if (type == 1)
+ CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, true, _ctx->bRes);
+ else if (type == 2)
+ CORO_INVOKE_2(_opt.initNoLoadSave, _bigBuf, _ctx->bRes);
+ else if (type == 3)
+ CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, false, _ctx->bRes);
+ else if (type == 4)
+ CORO_INVOKE_3(_opt.initSaveMenuOnly, _bigBuf, false, _ctx->bRes);
+
+ if (_ctx->bRes) {
+ g_vm->pauseSound(true);
+
+ disableInput();
+ _inv.endCombine();
+ _curActionObj = 0;
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ _point.setSpecialPointer(RMPointer::PTR_NONE);
+ _point.setCustomPointer(NULL);
+ enableMouse();
+ g_vm->grabThumbnail();
+
+ // Exists the IDLE to avoid premature death in loading
+ _bMustEnterMenu = true;
+ if (type == 1 || type == 2) {
+ GLOBALS._bIdleExited = true;
+ } else {
+ CORO_INVOKE_0(_tony.stopNoAction);
+
+ GLOBALS._bIdleExited = false;
+
+ CoroScheduler.createProcess(exitAllIdles, &_nCurLoc, sizeof(int));
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMGfxEngine::doFrame(CORO_PARAM, bool bDrawLocation) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Poll of input devices
+ _input.poll();
+
+ if (_bMustEnterMenu && GLOBALS._bIdleExited) {
+ _bOption = true;
+ _bMustEnterMenu = false;
+ GLOBALS._bIdleExited = false;
+ }
+
+ if (_bOption) {
+ CORO_INVOKE_1(_opt.doFrame, &_input);
+ _bOption = !_opt.isClosing();
+ if (!_bOption) {
+ disableMouse();
+ enableInput();
+ mpalStartIdlePoll(_nCurLoc);
+ g_vm->pauseSound(false);
+ }
+ }
+
+ if (bDrawLocation && _bLocationLoaded) {
+ // Location and objects
+ _loc.doFrame(&_bigBuf);
+
+ // Check the mouse input
+ if (_bInput && !_tony.inAction()) {
+ // If we are on the inventory, it is it who controls all input
+ if (_inv.haveFocus(_input.mousePos()) && !_inter.active()) {
+ // Left Click
+ // **********
+ if (_input.mouseLeftClicked()/* && m_itemName.IsItemSelected()*/) {
+ // Left click activates the combine, if we are on an object
+ if (_inv.leftClick(_input.mousePos(), _curActionObj)) {
+ _curAction = TA_COMBINE;
+ _point.setAction(_curAction);
+ }
+ } else
+
+ // Right Click
+ // ***********
+ if (_input.mouseRightClicked()) {
+ if (_itemName.isItemSelected()) {
+ _curActionObj = 0;
+ _inv.rightClick(_input.mousePos());
+ } else
+ _inv.rightClick(_input.mousePos());
+ } else
+
+ // Right Release
+ // *************
+ if (_input.mouseRightReleased()) {
+ if (_inv.rightRelease(_input.mousePos(), _curAction)) {
+ CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);
+
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ }
+ }
+ } else {
+ // Options Menu
+ // ************
+ if (_bGUIOption) {
+ if (!_tony.inAction() && _bInput) {
+ if ((_input.mouseLeftClicked() && _input.mousePos()._x < 3 && _input.mousePos()._y < 3)) {
+ CORO_INVOKE_1(openOptionScreen, 0);
+ goto SKIPCLICKSINISTRO;
+ } else if (_input.getAsyncKeyState(Common::KEYCODE_ESCAPE))
+ CORO_INVOKE_1(openOptionScreen, 0);
+ else if (!g_vm->getIsDemo()) {
+ if (_input.getAsyncKeyState(Common::KEYCODE_F3) || _input.getAsyncKeyState(Common::KEYCODE_F5))
+ // Save game screen
+ CORO_INVOKE_1(openOptionScreen, 4);
+ else if (_input.getAsyncKeyState(Common::KEYCODE_F2) || _input.getAsyncKeyState(Common::KEYCODE_F7))
+ // Load game screen
+ CORO_INVOKE_1(openOptionScreen, 3);
+ }
+ }
+ }
+
+ // Left Click
+ // **************
+ if (_input.mouseLeftClicked() && !_inter.active()) {
+
+ if (_curAction != TA_COMBINE)
+ CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _point.curAction());
+ else if (_itemName.getSelectedItem() != NULL)
+ CORO_INVOKE_4(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), TA_COMBINE, _curActionObj);
+
+ if (_curAction == TA_COMBINE) {
+ _inv.endCombine();
+ _point.setSpecialPointer(RMPointer::PTR_NONE);
+ }
+
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ }
+
+SKIPCLICKSINISTRO:
+ // Right Click
+ // ************
+ if (_curAction == TA_COMBINE) {
+ // During a combine, it cancels it
+ if (_input.mouseRightClicked()) {
+ _inv.endCombine();
+ _curActionObj = 0;
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ _point.setSpecialPointer(RMPointer::PTR_NONE);
+ }
+ } else if (_input.mouseRightClicked() && _itemName.isItemSelected() && _point.getSpecialPointer() == RMPointer::PTR_NONE) {
+ if (_bGUIInterface) {
+ // Before opening the interface, replaces GOTO
+ _curAction = TA_GOTO;
+ _curActionObj = 0;
+ _point.setAction(_curAction);
+ _inter.clicked(_input.mousePos());
+ }
+ }
+
+
+ // Right Release
+ // *************
+ if (_input.mouseRightReleased()) {
+ if (_bGUIInterface) {
+ if (_inter.released(_input.mousePos(), _curAction)) {
+ _point.setAction(_curAction);
+ CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);
+
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ }
+ }
+ }
+ }
+
+ // Update the name under the mouse pointer
+ _itemName.setMouseCoord(_input.mousePos());
+ if (!_inter.active() && !_inv.miniActive())
+ CORO_INVOKE_4(_itemName.doFrame, _bigBuf, _loc, _point, _inv);
+ }
+
+ // Interface & Inventory
+ _inter.doFrame(_bigBuf, _input.mousePos());
+ _inv.doFrame(_bigBuf, _point, _input.mousePos(), (!_tony.inAction() && !_inter.active() && _bGUIInventory));
+ }
+
+ // Animate Tony
+ CORO_INVOKE_2(_tony.doFrame, &_bigBuf, _nCurLoc);
+
+ // Update screen scrolling to keep Tony in focus
+ if (_tony.mustUpdateScrolling() && _bLocationLoaded) {
+ RMPoint showThis = _tony.position();
+ showThis._y -= 60;
+ _loc.updateScrolling(showThis);
+ }
+
+ if (_bLocationLoaded)
+ _tony.setScrollPosition(_loc.scrollPosition());
+
+ if ((!_tony.inAction() && _bInput) || _bAlwaysDrawMouse) {
+ _point.showCursor();
+ } else {
+ _point.hideCursor();
+ }
+ _point.doFrame();
+
+ // **********************
+ // Draw the list in the OT
+ // **********************
+ CORO_INVOKE_0(_bigBuf.drawOT);
+
+#define FSTEP (480/32)
+
+ // Wipe
+ if (_bWiping) {
+ switch (_nWipeType) {
+ case 1:
+ if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top >= FSTEP * 2)) {
+ CoroScheduler.setEvent(_hWipeEvent);
+ _nWipeType = 3;
+ break;
+ }
+
+ _rcWipeEllipse.top += FSTEP;
+ _rcWipeEllipse.left += FSTEP;
+ _rcWipeEllipse.right -= FSTEP;
+ _rcWipeEllipse.bottom -= FSTEP;
+ break;
+
+ case 2:
+ if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top < 480 - FSTEP)) {
+ CoroScheduler.setEvent(_hWipeEvent);
+ _nWipeType = 3;
+ break;
+ }
+
+ _rcWipeEllipse.top -= FSTEP;
+ _rcWipeEllipse.left -= FSTEP;
+ _rcWipeEllipse.right += FSTEP;
+ _rcWipeEllipse.bottom += FSTEP;
+ break;
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMGfxEngine::initCustomDll() {
+ setupGlobalVars(&_tony, &_point, &g_vm->_theBoxes, &_loc, &_inv, &_input);
+}
+
+void RMGfxEngine::itemIrq(uint32 dwItem, int nPattern, int nStatus) {
+ RMItem *item;
+ assert(GLOBALS._gfxEngine);
+
+ if (GLOBALS._gfxEngine->_bLocationLoaded) {
+ item = GLOBALS._gfxEngine->_loc.getItemFromCode(dwItem);
+ if (item != NULL) {
+ if (nPattern != -1) {
+ item->setPattern(nPattern, true);
+ }
+ if (nStatus != -1)
+ item->setStatus(nStatus);
+ }
+ }
+}
+
+void RMGfxEngine::initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) {
+ if (start._x == -1 || start._y == -1) {
+ start._x = ptTonyStart._x - RM_SX / 2;
+ start._y = ptTonyStart._y - RM_SY / 2;
+ }
+
+ _loc.setScrollPosition(start);
+
+ if (ptTonyStart._x == 0 && ptTonyStart._y == 0) {
+ } else {
+ _tony.setPosition(ptTonyStart, nLoc);
+ _tony.setScrollPosition(start);
+ }
+
+ _curAction = TA_GOTO;
+ _point.setCustomPointer(NULL);
+ _point.setSpecialPointer(RMPointer::PTR_NONE);
+ _point.setAction(_curAction);
+ _inter.reset();
+ _inv.reset();
+
+ mpalStartIdlePoll(_nCurLoc);
+}
+
+uint32 RMGfxEngine::loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) {
+ _nCurLoc = nLoc;
+
+ bool bLoaded = false;
+ for (int i = 0; i < 5; i++) {
+ // Try the loading of the location
+ RMRes res(_nCurLoc);
+ if (!res.isValid())
+ continue;
+
+ Common::SeekableReadStream *ds = res.getReadStream();
+ _loc.load(*ds);
+ delete ds;
+
+ initForNewLocation(nLoc, ptTonyStart, start);
+ bLoaded = true;
+ break;
+ }
+
+ if (!bLoaded)
+ error("Location was not loaded");
+
+ if (_bOption)
+ _opt.reInit(_bigBuf);
+
+ _bLocationLoaded = true;
+
+ // On entering the location
+ return CORO_INVALID_PID_VALUE; //mpalQueryDoAction(0, m_nCurLoc, 0);
+}
+
+void RMGfxEngine::unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result) {
+ CORO_BEGIN_CONTEXT;
+ uint32 h;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Release the location
+ CORO_INVOKE_2(mpalEndIdlePoll, _nCurLoc, NULL);
+
+ // On Exit?
+ if (bDoOnExit) {
+ _ctx->h = mpalQueryDoAction(1, _nCurLoc, 0);
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+ }
+
+ _bLocationLoaded = false;
+
+ _bigBuf.clearOT();
+ _loc.unload();
+
+ if (result != NULL)
+ *result = CORO_INVALID_PID_VALUE;
+
+ CORO_END_CODE;
+}
+
+void RMGfxEngine::init() {
+ // Screen loading
+ RMResRaw *raw;
+ RMGfxSourceBuffer16 *load = NULL;
+ INIT_GFX16_FROMRAW(20038, load);
+ _bigBuf.addPrim(new RMGfxPrimitive(load));
+ _bigBuf.drawOT(Common::nullContext);
+ _bigBuf.clearOT();
+ delete load;
+
+ // Display 'Loading' screen
+ _bigBuf.addDirtyRect(Common::Rect(0, 0, RM_SX, RM_SY));
+ g_vm->_window.getNewFrame(*this, NULL);
+ g_vm->_window.repaint();
+
+ // Activate GUI
+ _bGUIOption = true;
+ _bGUIInterface = true;
+ _bGUIInventory = true;
+
+ GLOBALS._bSkipSfxNoLoop = false;
+ _bMustEnterMenu = false;
+ GLOBALS._bIdleExited = false;
+ _bOption = false;
+ _bWiping = false;
+ _hWipeEvent = CoroScheduler.createEvent(false, false);
+
+ // Initialize the IRQ function for items for MPAL
+ GLOBALS._gfxEngine = this;
+ mpalInstallItemIrq(itemIrq);
+
+ // Initialize the mouse pointer
+ _point.init();
+
+ // Initialize Tony
+ _tony.init();
+ _tony.linkToBoxes(&g_vm->_theBoxes);
+
+ // Initialize the inventory and the interface
+ _inv.init();
+ _inter.init();
+
+ // Download the location and set priorities @@@@@
+ _bLocationLoaded = false;
+
+ enableInput();
+
+ // Starting the game
+ _tony.executeAction(20, 1, 0);
+}
+
+void RMGfxEngine::close() {
+ _bigBuf.clearOT();
+
+ _inter.close();
+ _inv.close();
+ _tony.close();
+ _point.close();
+}
+
+void RMGfxEngine::enableInput() {
+ _bInput = true;
+}
+
+void RMGfxEngine::disableInput() {
+ _bInput = false;
+ _inter.reset();
+}
+
+void RMGfxEngine::enableMouse() {
+ _bAlwaysDrawMouse = true;
+}
+
+void RMGfxEngine::disableMouse() {
+ _bAlwaysDrawMouse = false;
+}
+
+#define TONY_SAVEGAME_VERSION 8
+
+void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Common::String &name) {
+ Common::OutSaveFile *f;
+ byte *state;
+ char buf[4];
+ RMPoint tp = _tony.position();
+
+ // Saving: MPAL variables, current location, and Tony inventory position
+
+ // For now, we only save the MPAL state
+ uint size = mpalGetSaveStateSize();
+ state = new byte[size];
+ mpalSaveState(state);
+
+ uint thumbsize = 160 * 120 * 2;
+
+ buf[0] = 'R';
+ buf[1] = 'M';
+ buf[2] = 'S';
+ buf[3] = TONY_SAVEGAME_VERSION;
+
+ f = g_system->getSavefileManager()->openForSaving(fn);
+ if (f == NULL)
+ return;
+
+ f->write(buf, 4);
+ f->writeUint32LE(thumbsize);
+ f->write(curThumb, thumbsize);
+
+ // Difficulty level
+ int i = mpalQueryGlobalVar("VERSIONEFACILE");
+ f->writeByte(i);
+
+ i = strlen(name.c_str());
+ f->writeByte(i);
+ f->write(name.c_str(), i);
+ f->writeUint32LE(_nCurLoc);
+ f->writeUint32LE(tp._x);
+ f->writeUint32LE(tp._y);
+
+ f->writeUint32LE(size);
+ f->write(state, size);
+ delete[] state;
+
+ // Inventory
+ size = _inv.getSaveStateSize();
+ state = new byte[size];
+ _inv.saveState(state);
+ f->writeUint32LE(size);
+ f->write(state, size);
+ delete[] state;
+
+ // boxes
+ size = g_vm->_theBoxes.getSaveStateSize();
+ state = new byte[size];
+ g_vm->_theBoxes.saveState(state);
+ f->writeUint32LE(size);
+ f->write(state, size);
+ delete[] state;
+
+ // New Ver5
+ // Saves the state of the shepherdess and show yourself
+ bool bStat = _tony.getShepherdess();
+ f->writeByte(bStat);
+ bStat = _inter.getPerorate();
+ f->writeByte(bStat);
+
+ // Save the chars
+ charsSaveAll(f);
+
+ // Save the options
+ f->writeByte(GLOBALS._bCfgInvLocked);
+ f->writeByte(GLOBALS._bCfgInvNoScroll);
+ f->writeByte(GLOBALS._bCfgTimerizedText);
+ f->writeByte(GLOBALS._bCfgInvUp);
+ f->writeByte(GLOBALS._bCfgAnni30);
+ f->writeByte(GLOBALS._bCfgAntiAlias);
+ f->writeByte(GLOBALS._bShowSubtitles);
+ f->writeByte(GLOBALS._bCfgTransparence);
+ f->writeByte(GLOBALS._bCfgInterTips);
+ f->writeByte(GLOBALS._bCfgDubbing);
+ f->writeByte(GLOBALS._bCfgMusic);
+ f->writeByte(GLOBALS._bCfgSFX);
+ f->writeByte(GLOBALS._nCfgTonySpeed);
+ f->writeByte(GLOBALS._nCfgTextSpeed);
+ f->writeByte(GLOBALS._nCfgDubbingVolume);
+ f->writeByte(GLOBALS._nCfgMusicVolume);
+ f->writeByte(GLOBALS._nCfgSFXVolume);
+
+ // Save the hotspots
+ saveChangedHotspot(f);
+
+ // Save the music
+ saveMusic(f);
+
+ f->finalize();
+ delete f;
+}
+
+void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) {
+ // PROBLEM: You should change the location in a separate process to do the OnEnter
+ CORO_BEGIN_CONTEXT;
+ Common::InSaveFile *f;
+ byte *state, *statecmp;
+ uint size, sizecmp;
+ char buf[4];
+ RMPoint tp;
+ int loc;
+ int ver;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->f = g_system->getSavefileManager()->openForLoading(fn);
+ if (_ctx->f == NULL)
+ return;
+ _ctx->f->read(_ctx->buf, 4);
+
+ if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') {
+ delete _ctx->f;
+ return;
+ }
+
+ _ctx->ver = _ctx->buf[3];
+
+ if (_ctx->ver == 0 || _ctx->ver > TONY_SAVEGAME_VERSION) {
+ delete _ctx->f;
+ return;
+ }
+
+ if (_ctx->ver >= 0x3) {
+ // There is a thumbnail. If the version is between 5 and 7, it's compressed
+ if ((_ctx->ver >= 0x5) && (_ctx->ver <= 0x7)) {
+ _ctx->i = 0;
+ _ctx->i = _ctx->f->readUint32LE();
+ _ctx->f->seek(_ctx->i);
+ } else {
+ if (_ctx->ver >= 8)
+ // Skip thumbnail size
+ _ctx->f->skip(4);
+
+ _ctx->f->seek(160 * 120 * 2, SEEK_CUR);
+ }
+ }
+
+ if (_ctx->ver >= 0x5) {
+ // Skip the difficulty level
+ _ctx->f->seek(1, SEEK_CUR);
+ }
+
+ if (_ctx->ver >= 0x4) { // Skip the savegame name, which serves no purpose
+ _ctx->i = _ctx->f->readByte();
+ _ctx->f->seek(_ctx->i, SEEK_CUR);
+ }
+
+ _ctx->loc = _ctx->f->readUint32LE();
+ _ctx->tp._x = _ctx->f->readUint32LE();
+ _ctx->tp._y = _ctx->f->readUint32LE();
+ _ctx->size = _ctx->f->readUint32LE();
+
+ if ((_ctx->ver >= 0x5) && (_ctx->ver <= 7)) {
+ // MPAL was packed!
+ _ctx->sizecmp = _ctx->f->readUint32LE();
+ _ctx->state = new byte[_ctx->size];
+ _ctx->statecmp = new byte[_ctx->sizecmp];
+ _ctx->f->read(_ctx->statecmp, _ctx->sizecmp);
+ lzo1x_decompress(_ctx->statecmp, _ctx->sizecmp, _ctx->state, &_ctx->size);
+ delete[] _ctx->statecmp;
+ } else {
+ // Read uncompressed MPAL data
+ _ctx->state = new byte[_ctx->size];
+ _ctx->f->read(_ctx->state, _ctx->size);
+ }
+
+ mpalLoadState(_ctx->state);
+ delete[] _ctx->state;
+
+ // Inventory
+ _ctx->size = _ctx->f->readUint32LE();
+ _ctx->state = new byte[_ctx->size];
+ _ctx->f->read(_ctx->state, _ctx->size);
+ _inv.loadState(_ctx->state);
+ delete[] _ctx->state;
+
+ if (_ctx->ver >= 0x2) { // Version 2: box please
+ _ctx->size = _ctx->f->readUint32LE();
+ _ctx->state = new byte[_ctx->size];
+ _ctx->f->read(_ctx->state, _ctx->size);
+ g_vm->_theBoxes.loadState(_ctx->state);
+ delete[] _ctx->state;
+ }
+
+ if (_ctx->ver >= 5) {
+ // Version 5
+ bool bStat = false;
+
+ bStat = _ctx->f->readByte();
+ _tony.setShepherdess(bStat);
+ bStat = _ctx->f->readByte();
+ _inter.setPerorate(bStat);
+
+ charsLoadAll(_ctx->f);
+ }
+
+ if (_ctx->ver >= 6) {
+ // Load options
+ GLOBALS._bCfgInvLocked = _ctx->f->readByte();
+ GLOBALS._bCfgInvNoScroll = _ctx->f->readByte();
+ GLOBALS._bCfgTimerizedText = _ctx->f->readByte();
+ GLOBALS._bCfgInvUp = _ctx->f->readByte();
+ GLOBALS._bCfgAnni30 = _ctx->f->readByte();
+ GLOBALS._bCfgAntiAlias = _ctx->f->readByte();
+ GLOBALS._bShowSubtitles = _ctx->f->readByte();
+ GLOBALS._bCfgTransparence = _ctx->f->readByte();
+ GLOBALS._bCfgInterTips = _ctx->f->readByte();
+ GLOBALS._bCfgDubbing = _ctx->f->readByte();
+ GLOBALS._bCfgMusic = _ctx->f->readByte();
+ GLOBALS._bCfgSFX = _ctx->f->readByte();
+ GLOBALS._nCfgTonySpeed = _ctx->f->readByte();
+ GLOBALS._nCfgTextSpeed = _ctx->f->readByte();
+ GLOBALS._nCfgDubbingVolume = _ctx->f->readByte();
+ GLOBALS._nCfgMusicVolume = _ctx->f->readByte();
+ GLOBALS._nCfgSFXVolume = _ctx->f->readByte();
+
+ // Load hotspots
+ loadChangedHotspot(_ctx->f);
+ }
+
+ if (_ctx->ver >= 7) {
+ loadMusic(_ctx->f);
+ }
+
+ delete _ctx->f;
+
+ CORO_INVOKE_2(unloadLocation, false, NULL);
+ loadLocation(_ctx->loc, _ctx->tp, RMPoint(-1, -1));
+ _tony.setPattern(RMTony::PAT_STANDRIGHT);
+
+ // On older versions, need to an enter action
+ if (_ctx->ver < 5)
+ mpalQueryDoAction(0, _ctx->loc, 0);
+ else {
+ // In the new ones, we just reset the mcode
+ mCharResetCodes();
+ }
+
+ if (_ctx->ver >= 6)
+ reapplyChangedHotspot();
+
+ CORO_INVOKE_0(restoreMusic);
+
+ _bGUIInterface = true;
+ _bGUIInventory = true;
+ _bGUIOption = true;
+
+ CORO_END_CODE;
+}
+
+void RMGfxEngine::pauseSound(bool bPause) {
+ if (_bLocationLoaded)
+ _loc.pauseSound(bPause);
+}
+
+void RMGfxEngine::initWipe(int type) {
+ _bWiping = true;
+ _nWipeType = type;
+ _nWipeStep = 0;
+
+ if (_nWipeType == 1)
+ _rcWipeEllipse = Common::Rect(80, 0, 640 - 80, 480);
+ else if (_nWipeType == 2)
+ _rcWipeEllipse = Common::Rect(320 - FSTEP, 240 - FSTEP, 320 + FSTEP, 240 + FSTEP);
+}
+
+void RMGfxEngine::closeWipe() {
+ _bWiping = false;
+}
+
+void RMGfxEngine::waitWipeEnd(CORO_PARAM) {
+ CoroScheduler.waitForSingleObject(coroParam, _hWipeEvent, CORO_INFINITE);
+}
+
+bool RMGfxEngine::canLoadSave() {
+ return _bInput && !_tony.inAction() && !g_vm->getIsDemo();
+}
+
+RMGfxEngine::operator RMGfxTargetBuffer &() {
+ return _bigBuf;
+}
+
+RMInput &RMGfxEngine::getInput() {
+ return _input;
+}
+
+RMPointer &RMGfxEngine::getPointer() {
+ return _point;
+}
+
+/**
+ * Link to graphic task
+ */
+void RMGfxEngine::linkGraphicTask(RMGfxTask *task) {
+ _bigBuf.addPrim(new RMGfxPrimitive(task));
+}
+
+void RMGfxEngine::setPerorate(bool bpal) {
+ _inter.setPerorate(bpal);
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/gfxengine.h b/engines/tony/gfxengine.h
new file mode 100644
index 0000000000..ab32a01972
--- /dev/null
+++ b/engines/tony/gfxengine.h
@@ -0,0 +1,139 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_GFXENGINE_H
+#define TONY_GFXENGINE_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/rect.h"
+#include "tony/mpal/memory.h"
+#include "tony/game.h"
+#include "tony/gfxcore.h"
+#include "tony/input.h"
+#include "tony/inventory.h"
+#include "tony/tonychar.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+class RMGfxEngine {
+private:
+ RMGfxTargetBuffer _bigBuf;
+ RMInput _input;
+ RMPointer _point;
+ RMLocation _loc;
+ RMOptionScreen _opt;
+ RMTony _tony;
+ RMInventory _inv;
+ RMInterface _inter;
+ RMTextItemName _itemName;
+
+ bool _bOption;
+ bool _bLocationLoaded;
+
+ bool _bInput;
+ bool _bAlwaysDrawMouse;
+
+ int _nCurLoc;
+ RMTonyAction _curAction;
+ int _curActionObj;
+
+ int _nWipeType;
+ uint32 _hWipeEvent;
+ int _nWipeStep;
+
+ bool _bMustEnterMenu;
+protected:
+ static void itemIrq(uint32 dwItem, int nPattern, int nStatus);
+ void initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start);
+public:
+ bool _bWiping;
+ Common::Rect _rcWipeEllipse;
+ bool _bGUIOption;
+ bool _bGUIInterface;
+ bool _bGUIInventory;
+public:
+ RMGfxEngine();
+ virtual ~RMGfxEngine();
+
+ // Draw the next frame
+ void doFrame(CORO_PARAM, bool bDrawLocation);
+
+ // Initializes the graphics engine
+ void init();
+
+ // Closes the graphics engine
+ void close();
+
+ // Warns when entering or exits the options menu
+ void openOptionScreen(CORO_PARAM, int type);
+
+ // Enables or disables mouse input
+ void enableInput();
+ void disableInput();
+
+ // Enables and disables mouse draw
+ void enableMouse();
+ void disableMouse();
+
+ operator RMGfxTargetBuffer &();
+ RMInput &getInput();
+ RMPointer &getPointer();
+
+ // Link to the custom function list
+ void initCustomDll();
+
+ // Link to graphic task
+ void linkGraphicTask(RMGfxTask *task);
+
+ // Manage a location
+ uint32 loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start);
+ void unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result);
+ int getCurrentLocation() const { return _nCurLoc; }
+
+ // State management
+ void saveState(const Common::String &fn, byte *curThumb, const Common::String &name);
+ void loadState(CORO_PARAM, const Common::String &fn);
+
+ // Pauses sound
+ void pauseSound(bool bPause);
+
+ // Wipe
+ void initWipe(int type);
+ void closeWipe();
+ void waitWipeEnd(CORO_PARAM);
+
+ void setPerorate(bool bpal);
+
+ bool canLoadSave();
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/globals.cpp b/engines/tony/globals.cpp
new file mode 100644
index 0000000000..8e4ae240a0
--- /dev/null
+++ b/engines/tony/globals.cpp
@@ -0,0 +1,135 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/algorithm.h"
+#include "tony/globals.h"
+
+namespace Tony {
+
+Globals::Globals() {
+ _nextLoop = false;
+ _nextChannel = 0;
+ _nextSync = 0;
+ _curChannel = 0;
+ _flipflop = 0;
+ _curBackText = NULL;
+ _bTonyIsSpeaking = false;
+ _curChangedHotspot = 0;
+ _tony = NULL;
+ _pointer = NULL;
+ _boxes = NULL;
+ _loc = NULL;
+ _inventory = NULL;
+ _input = NULL;
+ _gfxEngine = NULL;
+ EnableGUI = NULL;
+ DisableGUI = NULL;
+
+ _dwTonyNumTexts = 0;
+ _bTonyInTexts = false;
+ _bStaticTalk = false;
+ _bAlwaysDisplay = false;
+ _bIdleExited = false;
+ _bSkipSfxNoLoop = false;
+ _bNoBullsEye = false;
+ _curDialog = 0;
+ _curSoundEffect = 0;
+ _bFadeOutStop = false;
+
+ _bSkipIdle = false;
+ _hSkipIdle = 0;
+ _lastMusic = 0;
+ _lastTappeto = 0;
+ Common::fill(&_ambiance[0], &_ambiance[200], 0);
+ _fullScreenMessageLoc = 0;
+
+ // MPAL global variables
+ _mpalError = 0;
+ _lpiifCustom = NULL;
+ _lplpFunctions = NULL;
+ _lplpFunctionStrings = NULL;
+ _nObjs = 0;
+ _nVars = 0;
+ _hVars = NULL;
+ _lpmvVars = NULL;
+ _nMsgs = 0;
+ _hMsgs = NULL;
+ _lpmmMsgs = NULL;
+ _nDialogs = 0;
+ _hDialogs = NULL;
+ _lpmdDialogs = NULL;
+ _nItems = 0;
+ _hItems = NULL;
+ _lpmiItems = NULL;
+ _nLocations = 0;
+ _hLocations = NULL;
+ _lpmlLocations = NULL;
+ _nScripts = 0;
+ _hScripts = NULL;
+ _lpmsScripts = NULL;
+ _nResources = 0;
+ _lpResources = NULL;
+ _bExecutingAction = false;
+ _bExecutingDialog = false;
+ Common::fill(&_nPollingLocations[0], &_nPollingLocations[MAXPOLLINGLOCATIONS], 0);
+ Common::fill(&_hEndPollingLocations[0], &_hEndPollingLocations[MAXPOLLINGLOCATIONS], 0);
+ Common::fill(&_pollingThreads[0], &_pollingThreads[MAXPOLLINGLOCATIONS], 0);
+ _hAskChoice = 0;
+ _hDoneChoice = 0;
+ _nExecutingAction = 0;
+ _nExecutingDialog = 0;
+ _nExecutingChoice = 0;
+ _nSelectedChoice = 0;
+ _nTonyNextTalkType = RMTony::TALK_NORMAL;
+ _saveTonyLoc = 0;
+
+ for (int i = 0; i < 16; ++i) {
+ Common::fill((byte *)&_character[i], (byte *)&_character[i] + sizeof(CharacterStruct), 0);
+ _isMChar[i] = false;
+ }
+
+ for (int i = 0; i < 10; ++i)
+ Common::fill((byte *)&_mCharacter[i], (byte *)&_mCharacter[i] + sizeof(MCharacterStruct), 0);
+ for (int i = 0; i < 256; ++i)
+ Common::fill((byte *)&_changedHotspot[i], (byte *)&_changedHotspot[i] + sizeof(ChangedHotspotStruct), 0);
+
+ // Set up globals that have explicit initial values
+ _bCfgInvLocked = false;
+ _bCfgInvNoScroll = false;
+ _bCfgTimerizedText = true;
+ _bCfgInvUp = false;
+ _bCfgAnni30 = false;
+ _bCfgAntiAlias = false;
+ _bCfgTransparence = true;
+ _bCfgInterTips = true;
+ _bShowSubtitles = true;
+ _nCfgTonySpeed = 3;
+ _nCfgTextSpeed = 5;
+ _bCfgDubbing = true;
+ _bCfgMusic = true;
+ _bCfgSFX = true;
+ _nCfgDubbingVolume = 10;
+ _nCfgMusicVolume = 7;
+ _nCfgSFXVolume = 10;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/globals.h b/engines/tony/globals.h
new file mode 100644
index 0000000000..d8d8d3eba5
--- /dev/null
+++ b/engines/tony/globals.h
@@ -0,0 +1,286 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef TONY_GLOBALS
+#define TONY_GLOBALS
+
+#include "common/savefile.h"
+#include "tony/gfxengine.h"
+#include "tony/input.h"
+#include "tony/inventory.h"
+#include "tony/loc.h"
+#include "tony/tonychar.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/mpaldll.h"
+
+namespace Tony {
+
+#define AMBIANCE_CRICKETS 1
+#define AMBIANCE_CRICKETSMUFFLED 2
+#define AMBIANCE_CRICKETSWIND 3
+#define AMBIANCE_CRICKETSWIND1 4
+#define AMBIANCE_WIND 5
+#define AMBIANCE_SEA 6
+#define AMBIANCE_SEAHALFVOLUME 7
+
+struct CharacterStruct {
+ uint32 _code;
+ RMItem *_item;
+ byte _r, _g, _b;
+ int _talkPattern;
+ int _standPattern;
+ int _startTalkPattern, _endTalkPattern;
+ int _numTexts;
+
+ void save(Common::OutSaveFile *f) {
+ f->writeUint32LE(_code);
+ f->writeUint32LE(0);
+ f->writeByte(_r);
+ f->writeByte(_g);
+ f->writeByte(_b);
+ f->writeUint32LE(_talkPattern);
+ f->writeUint32LE(_standPattern);
+ f->writeUint32LE(_startTalkPattern);
+ f->writeUint32LE(_endTalkPattern);
+ f->writeUint32LE(_numTexts);
+ }
+ void load(Common::InSaveFile *f) {
+ _code = f->readUint32LE();
+ f->readUint32LE();
+ _item = NULL;
+ _r = f->readByte();
+ _g = f->readByte();
+ _b = f->readByte();
+ _talkPattern = f->readUint32LE();
+ _standPattern = f->readUint32LE();
+ _startTalkPattern = f->readUint32LE();
+ _endTalkPattern = f->readUint32LE();
+ _numTexts = f->readUint32LE();
+ }
+};
+
+struct MCharacterStruct {
+ uint32 _code;
+ RMItem *_item;
+ byte _r, _g, _b;
+ int _x, _y;
+ int _numTalks[10];
+ int _curGroup;
+ int _numTexts;
+ bool _bInTexts;
+ int _curTalk;
+ bool _bAlwaysBack;
+
+ void save(Common::OutSaveFile *f) {
+ f->writeUint32LE(_code);
+ f->writeUint32LE(0);
+ f->writeByte(_r);
+ f->writeByte(_g);
+ f->writeByte(_b);
+ f->writeUint32LE(_x);
+ f->writeUint32LE(_y);
+ for (int i = 0; i < 10; ++i)
+ f->writeUint32LE(_numTalks[i]);
+ f->writeUint32LE(_curGroup);
+ f->writeUint32LE(_numTexts);
+ f->writeByte(_bInTexts);
+ f->writeUint32LE(_curTalk);
+ f->writeByte(_bAlwaysBack);
+ }
+ void load(Common::InSaveFile *f) {
+ _code = f->readUint32LE();
+ f->readUint32LE();
+ _item = NULL;
+ _r = f->readByte();
+ _g = f->readByte();
+ _b = f->readByte();
+ _x = f->readUint32LE();
+ _y = f->readUint32LE();
+ for (int i = 0; i < 10; ++i)
+ _numTalks[i] = f->readUint32LE();
+ _curGroup = f->readUint32LE();
+ _numTexts = f->readUint32LE();
+ _bInTexts = f->readByte();
+ _curTalk = f->readUint32LE();
+ _bAlwaysBack = f->readByte();
+ }
+};
+
+struct ChangedHotspotStruct {
+ uint32 _dwCode;
+ uint32 _nX, _nY;
+
+ void save(Common::OutSaveFile *f) {
+ f->writeUint32LE(_dwCode);
+ f->writeUint32LE(_nX);
+ f->writeUint32LE(_nY);
+ }
+ void load(Common::InSaveFile *f) {
+ _dwCode = f->readUint32LE();
+ _nX = f->readUint32LE();
+ _nY = f->readUint32LE();
+ }
+};
+
+
+/**
+ * Description of a call to a custom function.
+ */
+typedef struct {
+ int _nCf;
+ int _arg1, _arg2, _arg3, _arg4;
+} CfCall;
+
+typedef CfCall *LpCfCall;
+
+struct CoroutineMutex {
+ CoroutineMutex() : _eventId(0), _ownerPid(0), _lockCount(0) { }
+
+ uint32 _eventId;
+ uint32 _ownerPid;
+ uint32 _lockCount;
+};
+
+/****************************************************************************\
+* Global variables
+\****************************************************************************/
+
+/**
+ * Globals class
+ */
+class Globals {
+public:
+ Globals();
+
+ Common::String _nextMusic;
+ bool _nextLoop;
+ int _nextChannel;
+ int _nextSync;
+ int _curChannel;
+ int _flipflop;
+ CharacterStruct _character[16];
+ MCharacterStruct _mCharacter[10];
+ ChangedHotspotStruct _changedHotspot[256];
+ bool _isMChar[16];
+ bool _bAlwaysDisplay;
+ RMPoint _saveTonyPos;
+ int _saveTonyLoc;
+ RMTextDialog *_curBackText;
+ bool _bTonyIsSpeaking;
+ int _curChangedHotspot;
+ bool _bCfgInvLocked;
+ bool _bCfgInvNoScroll;
+ bool _bCfgTimerizedText;
+ bool _bCfgInvUp;
+ bool _bCfgAnni30;
+ bool _bCfgAntiAlias;
+ bool _bShowSubtitles;
+ bool _bCfgTransparence;
+ bool _bCfgInterTips;
+ bool _bCfgDubbing;
+ bool _bCfgMusic;
+ bool _bCfgSFX;
+ int _nCfgTonySpeed;
+ int _nCfgTextSpeed;
+ int _nCfgDubbingVolume;
+ int _nCfgMusicVolume;
+ int _nCfgSFXVolume;
+ bool _bSkipSfxNoLoop;
+ bool _bIdleExited;
+ bool _bNoBullsEye;
+ int _curDialog;
+ int _curSoundEffect;
+ bool _bFadeOutStop;
+
+ RMTony *_tony;
+ RMPointer *_pointer;
+ RMGameBoxes *_boxes;
+ RMLocation *_loc;
+ RMInventory *_inventory;
+ RMInput *_input;
+ RMGfxEngine *_gfxEngine;
+
+ void (*EnableGUI)();
+ void (*DisableGUI)();
+
+ uint32 _dwTonyNumTexts;
+ bool _bTonyInTexts;
+ bool _bStaticTalk;
+ RMTony::CharacterTalkType _nTonyNextTalkType;
+
+ RMPoint _startLocPos[256];
+ CoroutineMutex _mut[10];
+
+ bool _bSkipIdle;
+ uint32 _hSkipIdle;
+
+ int _lastMusic, _lastTappeto;
+
+ int _ambiance[200];
+ RMPoint _fullScreenMessagePt;
+ int _fullScreenMessageLoc;
+
+ /**
+ * @defgroup MPAL variables
+ */
+ uint32 _mpalError;
+ LPITEMIRQFUNCTION _lpiifCustom;
+ LPLPCUSTOMFUNCTION _lplpFunctions;
+ Common::String *_lplpFunctionStrings;
+ uint16 _nObjs;
+ uint16 _nVars;
+ MpalHandle _hVars;
+ LpMpalVar _lpmvVars;
+ uint16 _nMsgs;
+ MpalHandle _hMsgs;
+ LpMpalMsg _lpmmMsgs;
+ uint16 _nDialogs;
+ MpalHandle _hDialogs;
+ LpMpalDialog _lpmdDialogs;
+ uint16 _nItems;
+ MpalHandle _hItems;
+ LpMpalItem _lpmiItems;
+ uint16 _nLocations;
+ MpalHandle _hLocations;
+ LpMpalLocation _lpmlLocations;
+ uint16 _nScripts;
+ MpalHandle _hScripts;
+ LpMpalScript _lpmsScripts;
+ Common::File _hMpr;
+ uint16 _nResources;
+ uint32 *_lpResources;
+ bool _bExecutingAction;
+ bool _bExecutingDialog;
+ uint32 _nPollingLocations[MAXPOLLINGLOCATIONS];
+ uint32 _hEndPollingLocations[MAXPOLLINGLOCATIONS];
+ uint32 _pollingThreads[MAXPOLLINGLOCATIONS];
+ uint32 _hAskChoice;
+ uint32 _hDoneChoice;
+ uint32 _nExecutingAction;
+ uint32 _nExecutingDialog;
+ uint32 _nExecutingChoice;
+ uint32 _nSelectedChoice;
+};
+
+} // End of namespace Tony
+
+#endif // TONY_GLOBALS
diff --git a/engines/tony/input.cpp b/engines/tony/input.cpp
new file mode 100644
index 0000000000..b96ccaf842
--- /dev/null
+++ b/engines/tony/input.cpp
@@ -0,0 +1,157 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/gfxengine.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+RMInput::RMInput() {
+ // Setup mouse fields
+ _clampMouse = false;
+ _mousePos.set(0, 0);
+ _leftButton = _rightButton = false;
+ _leftClickMouse = _leftReleaseMouse = false;
+ _rightClickMouse = _rightReleaseMouse = false;
+
+ Common::fill((byte *)&_event, (byte *)&_event + sizeof(Common::Event), 0);
+
+ // Setup keyboard fields
+ Common::fill(&_keyDown[0], &_keyDown[350], 0);
+}
+
+RMInput::~RMInput() {
+}
+
+void RMInput::poll() {
+ _leftClickMouse = _leftReleaseMouse = _rightClickMouse = _rightReleaseMouse = false;
+
+ // Get pending events
+ while (g_system->getEventManager()->pollEvent(_event) && !g_vm->shouldQuit()) {
+ switch (_event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONDOWN:
+ case Common::EVENT_RBUTTONUP:
+ _mousePos.set(_event.mouse.x, _event.mouse.y);
+
+ if (_event.type == Common::EVENT_LBUTTONDOWN) {
+ _leftButton = true;
+ _leftClickMouse = true;
+ } else if (_event.type == Common::EVENT_LBUTTONUP) {
+ _leftButton = false;
+ _leftReleaseMouse = true;
+ } else if (_event.type == Common::EVENT_RBUTTONDOWN) {
+ _rightButton = true;
+ _rightClickMouse = true;
+ } else if (_event.type == Common::EVENT_RBUTTONUP) {
+ _rightButton = false;
+ _rightReleaseMouse = true;
+ } else
+ continue;
+
+ // Since a mouse button has changed, don't do any further event processing this frame
+ return;
+
+ case Common::EVENT_KEYDOWN:
+ // Check for debugger
+ if ((_event.kbd.keycode == Common::KEYCODE_d) && (_event.kbd.flags & Common::KBD_CTRL)) {
+ // Attach to the debugger
+ g_vm->_debugger->attach();
+ g_vm->_debugger->onFrame();
+ } else {
+ // Flag the given key as being down
+ _keyDown[(int)_event.kbd.keycode] = true;
+ }
+ return;
+
+ case Common::EVENT_KEYUP:
+ _keyDown[(int)_event.kbd.keycode] = false;
+ return;
+
+ default:
+ break;
+ }
+ }
+}
+
+bool RMInput::mouseLeft() {
+ return _leftButton;
+}
+
+bool RMInput::mouseRight() {
+ return _rightButton;
+}
+
+/**
+ * Return true if a key has been pressed
+ */
+bool RMInput::getAsyncKeyState(Common::KeyCode kc) {
+ // The act of testing for a particular key automatically clears the state, to prevent
+ // the same key being registered in multiple different frames
+ bool result = _keyDown[(int)kc];
+ _keyDown[(int)kc] = false;
+ return result;
+}
+
+/**
+ * Reading of the mouse
+ */
+RMPoint RMInput::mousePos() {
+ return _mousePos;
+}
+
+/**
+ * Events of mouse clicks
+ */
+bool RMInput::mouseLeftClicked() {
+ return _leftClickMouse;
+}
+
+bool RMInput::mouseRightClicked() {
+ return _rightClickMouse;
+}
+
+bool RMInput::mouseBothClicked() {
+ return _leftClickMouse && _rightClickMouse;
+}
+
+bool RMInput::mouseLeftReleased() {
+ return _leftReleaseMouse;
+}
+
+bool RMInput::mouseRightReleased() {
+ return _rightReleaseMouse;
+}
+
+bool RMInput::mouseBothReleased() {
+ return _leftReleaseMouse && _rightReleaseMouse;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/input.h b/engines/tony/input.h
new file mode 100644
index 0000000000..d07eaefe34
--- /dev/null
+++ b/engines/tony/input.h
@@ -0,0 +1,88 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_INPUT_H
+#define TONY_INPUT_H
+
+#include "common/events.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+class RMInput {
+private:
+ Common::Event _event;
+
+ // Mouse related fields
+ RMPoint _mousePos;
+ bool _clampMouse;
+ bool _leftButton, _rightButton;
+ bool _leftClickMouse, _leftReleaseMouse, _rightClickMouse, _rightReleaseMouse;
+
+ // Keyboard related fields
+ bool _keyDown[350];
+
+public:
+ RMInput();
+ ~RMInput();
+
+ /**
+ * Polling (must be performed once per frame)
+ */
+ void poll();
+
+ /**
+ * Reading of the mouse
+ */
+ RMPoint mousePos();
+
+ /**
+ * Current status of the mouse buttons
+ */
+ bool mouseLeft();
+ bool mouseRight();
+
+ /**
+ * Events of mouse clicks
+ */
+ bool mouseLeftClicked();
+ bool mouseRightClicked();
+ bool mouseBothClicked();
+ bool mouseLeftReleased();
+ bool mouseRightReleased();
+ bool mouseBothReleased();
+
+ /**
+ * Returns true if the given key is pressed
+ */
+ bool getAsyncKeyState(Common::KeyCode kc);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/inventory.cpp b/engines/tony/inventory.cpp
new file mode 100644
index 0000000000..12540e5b7f
--- /dev/null
+++ b/engines/tony/inventory.cpp
@@ -0,0 +1,938 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/textconsole.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/inventory.h"
+#include "tony/game.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+
+/****************************************************************************\
+* RMInventory Methods
+\****************************************************************************/
+
+RMInventory::RMInventory() {
+ _items = NULL;
+ _state = CLOSED;
+ _bCombining = false;
+ _csModifyInterface = g_system->createMutex();
+ _nItems = 0;
+
+ Common::fill(_inv, _inv + 256, 0);
+ _nInv = 0;
+ _curPutY = 0;
+ _curPutTime = 0;
+ _curPos = 0;
+ _bHasFocus = false;
+ _nSelectObj = 0;
+ _nCombine = 0;
+ _bBlinkingRight = false;
+ _bBlinkingLeft = false;
+ _miniAction = 0;
+}
+
+RMInventory::~RMInventory() {
+ close();
+ g_system->deleteMutex(_csModifyInterface);
+}
+
+bool RMInventory::checkPointInside(const RMPoint &pt) {
+ if (!GLOBALS._bCfgInvUp)
+ return pt._y > RM_SY - 70;
+ else
+ return pt._y < 70;
+}
+
+
+void RMInventory::init() {
+ // Create the main buffer
+ create(RM_SX, 68);
+ setPriority(185);
+
+ // Setup the inventory
+ _nInv = 0;
+ _curPos = 0;
+ _bCombining = false;
+
+ // New items
+ _nItems = 78; // @@@ Number of takeable items
+ _items = new RMInventoryItem[_nItems + 1];
+
+ int curres = 10500;
+
+ // Loop through the items
+ for (int i = 0; i <= _nItems; i++) {
+ // Load the items from the resource
+ RMRes res(curres);
+ assert(res.isValid());
+ Common::SeekableReadStream *ds = res.getReadStream();
+
+ // Initialize the MPAL inventory item by reading it in.
+ _items[i]._icon.setInitCurPattern(false);
+ _items[i]._icon.readFromStream(*ds);
+ delete ds;
+
+ // Puts in the default pattern 1
+ _items[i]._pointer = NULL;
+ _items[i]._status = 1;
+ _items[i]._icon.setPattern(1);
+ _items[i]._icon.doFrame(this, false);
+
+ curres++;
+ if (i == 0 || i == 28 || i == 29)
+ continue;
+
+ _items[i]._pointer = new RMGfxSourceBuffer8RLEByteAA[_items[i]._icon.numPattern()];
+
+ for (int j = 0; j < _items[i]._icon.numPattern(); j++) {
+ RMResRaw raw(curres);
+
+ assert(raw.isValid());
+
+ _items[i]._pointer[j].init((const byte *)raw, raw.width(), raw.height(), true);
+ curres++;
+ }
+ }
+
+ _items[28]._icon.setPattern(1);
+ _items[29]._icon.setPattern(1);
+
+ // Download interface
+ RMRes res(RES_I_MINIINTER);
+ assert(res.isValid());
+ Common::SeekableReadStream *ds = res.getReadStream();
+ _miniInterface.readFromStream(*ds);
+ _miniInterface.setPattern(1);
+ delete ds;
+
+ // Create the text for hints on the mini interface
+ _hints[0].setAlignType(RMText::HCENTER, RMText::VTOP);
+ _hints[1].setAlignType(RMText::HCENTER, RMText::VTOP);
+ _hints[2].setAlignType(RMText::HCENTER, RMText::VTOP);
+
+ // The text is taken from MPAL for translation
+ RMMessage msg1(15);
+ RMMessage msg2(13);
+ RMMessage msg3(14);
+
+ _hints[0].writeText(msg1[0], 1); // Examine
+ _hints[1].writeText(msg2[0], 1); // Take
+ _hints[2].writeText(msg3[0], 1); // Use
+
+
+ // Prepare initial inventory
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+}
+
+void RMInventory::close() {
+ // Has memory
+ if (_items != NULL) {
+ // Delete the item pointers
+ for (int i = 0; i <= _nItems; i++)
+ delete[] _items[i]._pointer;
+
+ // Delete the items array
+ delete[] _items;
+ _items = NULL;
+ }
+
+ destroy();
+}
+
+void RMInventory::reset() {
+ _state = CLOSED;
+ endCombine();
+}
+
+void RMInventory::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ RMPoint pos;
+ RMPoint pos2;
+ RMGfxPrimitive *p;
+ RMGfxPrimitive *p2;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ prim->setDst(RMPoint(0, _curPutY));
+ g_system->lockMutex(_csModifyInterface);
+ CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
+ g_system->unlockMutex(_csModifyInterface);
+
+ if (_state == SELECTING) {
+
+ if (!GLOBALS._bCfgInvUp) {
+ _ctx->pos.set((_nSelectObj + 1) * 64 - 20, RM_SY - 113);
+ _ctx->pos2.set((_nSelectObj + 1) * 64 + 34, RM_SY - 150);
+ } else {
+ _ctx->pos.set((_nSelectObj + 1) * 64 - 20, 72 - 4); // The brown part is at the top :(
+ _ctx->pos2.set((_nSelectObj + 1) * 64 + 34, 119 - 4);
+ }
+
+ _ctx->p = new RMGfxPrimitive(prim->_task, _ctx->pos);
+ _ctx->p2 = new RMGfxPrimitive(prim->_task, _ctx->pos2);
+
+ // Draw the mini interface
+ CORO_INVOKE_2(_miniInterface.draw, bigBuf, _ctx->p);
+
+ if (GLOBALS._bCfgInterTips) {
+ if (_miniAction == 1) // Examine
+ CORO_INVOKE_2(_hints[0].draw, bigBuf, _ctx->p2);
+ else if (_miniAction == 2) // Talk
+ CORO_INVOKE_2(_hints[1].draw, bigBuf, _ctx->p2);
+ else if (_miniAction == 3) // Use
+ CORO_INVOKE_2(_hints[2].draw, bigBuf, _ctx->p2);
+ }
+
+ delete _ctx->p;
+ delete _ctx->p2;
+ }
+
+ CORO_END_CODE;
+}
+
+void RMInventory::removeThis(CORO_PARAM, bool &result) {
+ if (_state == CLOSED)
+ result = true;
+ else
+ result = false;
+}
+
+void RMInventory::removeItem(int code) {
+ for (int i = 0; i < _nInv; i++) {
+ if (_inv[i] == code - 10000) {
+ g_system->lockMutex(_csModifyInterface);
+
+ Common::copy(&_inv[i + 1], &_inv[i + 1] + (_nInv - i), &_inv[i]);
+ _nInv--;
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ return;
+ }
+ }
+}
+
+void RMInventory::addItem(int code) {
+ if (code <= 10000 || code >= 10101) {
+ // If we are here, it means that we are adding an item that should not be in the inventory
+ warning("RMInventory::addItem(%d) - Cannot find a valid icon for this item, and then it will not be added to the inventory", code);
+ } else {
+ g_system->lockMutex(_csModifyInterface);
+ if (_curPos + 8 == _nInv) {
+ // Break through the inventory! On the flashing pattern
+ _items[28]._icon.setPattern(2);
+ }
+
+ _inv[_nInv++] = code - 10000;
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+}
+
+void RMInventory::changeItemStatus(uint32 code, uint32 dwStatus) {
+ if (code <= 10000 || code >= 10101) {
+ error("RMInventory::changeItemStatus(%d) - Specified object code is not valid", code);
+ } else {
+ g_system->lockMutex(_csModifyInterface);
+ _items[code - 10000]._icon.setPattern(dwStatus);
+ _items[code - 10000]._status = dwStatus;
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+}
+
+
+void RMInventory::prepare() {
+ for (int i = 1; i < RM_SX / 64 - 1; i++) {
+ if (i - 1 + _curPos < _nInv)
+ addPrim(new RMGfxPrimitive(&_items[_inv[i - 1 + _curPos]]._icon, RMPoint(i * 64, 0)));
+ else
+ addPrim(new RMGfxPrimitive(&_items[0]._icon, RMPoint(i * 64, 0)));
+ }
+
+ // Frecce
+ addPrim(new RMGfxPrimitive(&_items[29]._icon, RMPoint(0, 0)));
+ addPrim(new RMGfxPrimitive(&_items[28]._icon, RMPoint(640 - 64, 0)));
+}
+
+bool RMInventory::miniActive() {
+ return _state == SELECTING;
+}
+
+bool RMInventory::haveFocus(const RMPoint &mpos) {
+ // When we combine, have the focus only if we are on an arrow (to scroll)
+ if (_state == OPENED && _bCombining && checkPointInside(mpos) && (mpos._x < 64 || mpos._x > RM_SX - 64))
+ return true;
+
+ // If the inventory is open, focus we we go over it
+ if (_state == OPENED && !_bCombining && checkPointInside(mpos))
+ return true;
+
+ // If we are selecting a verb (and then right down), we always focus
+ if (_state == SELECTING)
+ return true;
+
+ return false;
+}
+
+void RMInventory::endCombine() {
+ _bCombining = false;
+}
+
+bool RMInventory::leftClick(const RMPoint &mpos, int &nCombineObj) {
+ // The left click picks an item from your inventory to use it with the background
+ int n = mpos._x / 64;
+
+ if (_state == OPENED) {
+ if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) {
+ _bCombining = true; //m_state = COMBINING;
+ _nCombine = _inv[n - 1 + _curPos];
+ nCombineObj = _nCombine + 10000;
+
+ g_vm->playUtilSFX(1);
+ return true;
+ }
+ }
+
+ // Click the right arrow
+ if ((_state == OPENED) && _bBlinkingRight) {
+ g_system->lockMutex(_csModifyInterface);
+ _curPos++;
+
+ if (_curPos + 8 >= _nInv) {
+ _bBlinkingRight = false;
+ _items[28]._icon.setPattern(1);
+ }
+
+ if (_curPos > 0) {
+ _bBlinkingLeft = true;
+ _items[29]._icon.setPattern(2);
+ }
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+ // Click the left arrow
+ else if ((_state == OPENED) && _bBlinkingLeft) {
+ assert(_curPos > 0);
+ g_system->lockMutex(_csModifyInterface);
+ _curPos--;
+
+ if (_curPos == 0) {
+ _bBlinkingLeft = false;
+ _items[29]._icon.setPattern(1);
+ }
+
+ if (_curPos + 8 < _nInv) {
+ _bBlinkingRight = true;
+ _items[28]._icon.setPattern(2);
+ }
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+
+
+ return false;
+}
+
+void RMInventory::rightClick(const RMPoint &mpos) {
+ assert(checkPointInside(mpos));
+
+ if (_state == OPENED && !_bCombining) {
+ // Open the context interface
+ int n = mpos._x / 64;
+
+ if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) {
+ _state = SELECTING;
+ _miniAction = 0;
+ _nSelectObj = n - 1;
+
+ g_vm->playUtilSFX(0);
+ }
+ }
+
+ if ((_state == OPENED) && _bBlinkingRight) {
+ g_system->lockMutex(_csModifyInterface);
+ _curPos += 7;
+ if (_curPos + 8 > _nInv)
+ _curPos = _nInv - 8;
+
+ if (_curPos + 8 <= _nInv) {
+ _bBlinkingRight = false;
+ _items[28]._icon.setPattern(1);
+ }
+
+ if (_curPos > 0) {
+ _bBlinkingLeft = true;
+ _items[29]._icon.setPattern(2);
+ }
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ } else if ((_state == OPENED) && _bBlinkingLeft) {
+ assert(_curPos > 0);
+ g_system->lockMutex(_csModifyInterface);
+ _curPos -= 7;
+ if (_curPos < 0)
+ _curPos = 0;
+
+ if (_curPos == 0) {
+ _bBlinkingLeft = false;
+ _items[29]._icon.setPattern(1);
+ }
+
+ if (_curPos + 8 < _nInv) {
+ _bBlinkingRight = true;
+ _items[28]._icon.setPattern(2);
+ }
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+}
+
+bool RMInventory::rightRelease(const RMPoint &mpos, RMTonyAction &curAction) {
+ if (_state == SELECTING) {
+ _state = OPENED;
+
+ if (_miniAction == 1) { // Examine
+ curAction = TA_EXAMINE;
+ return true;
+ } else if (_miniAction == 2) { // Talk
+ curAction = TA_TALK;
+ return true;
+ } else if (_miniAction == 3) { // Use
+ curAction = TA_USE;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#define INVSPEED 20
+
+void RMInventory::doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen) {
+ bool bNeedRedraw = false;
+
+ if (_state != CLOSED) {
+ // Clean up the OT list
+ g_system->lockMutex(_csModifyInterface);
+ clearOT();
+
+ // DoFrame makes all the objects currently in the inventory be displayed
+ // @@@ Maybe we should do all takeable objects? Please does not help
+ for (int i = 0; i < _nInv; i++) {
+ if (_items[_inv[i]]._icon.doFrame(this, false) && (i >= _curPos && i <= _curPos + 7))
+ bNeedRedraw = true;
+ }
+
+ if ((_state == CLOSING || _state == OPENING || _state == OPENED) && checkPointInside(mpos)) {
+ if (mpos._x > RM_SX - 64) {
+ if (_curPos + 8 < _nInv && !_bBlinkingRight) {
+ _items[28]._icon.setPattern(3);
+ _bBlinkingRight = true;
+ bNeedRedraw = true;
+ }
+ } else if (_bBlinkingRight) {
+ _items[28]._icon.setPattern(2);
+ _bBlinkingRight = false;
+ bNeedRedraw = true;
+ }
+
+ if (mpos._x < 64) {
+ if (_curPos > 0 && !_bBlinkingLeft) {
+ _items[29]._icon.setPattern(3);
+ _bBlinkingLeft = true;
+ bNeedRedraw = true;
+ }
+ } else if (_bBlinkingLeft) {
+ _items[29]._icon.setPattern(2);
+ _bBlinkingLeft = false;
+ bNeedRedraw = true;
+ }
+ }
+
+ if (_items[28]._icon.doFrame(this, false))
+ bNeedRedraw = true;
+
+ if (_items[29]._icon.doFrame(this, false))
+ bNeedRedraw = true;
+
+ if (bNeedRedraw)
+ prepare();
+
+ g_system->unlockMutex(_csModifyInterface);
+ }
+
+ if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_i)) {
+ GLOBALS._bCfgInvLocked = !GLOBALS._bCfgInvLocked;
+ }
+
+ if (_bCombining) {//m_state == COMBINING)
+ ptr.setCustomPointer(&_items[_nCombine]._pointer[_items[_nCombine]._status - 1]);
+ ptr.setSpecialPointer(RMPointer::PTR_CUSTOM);
+ }
+
+ if (!GLOBALS._bCfgInvUp) {
+ if ((_state == CLOSED) && (mpos._y > RM_SY - 10 || GLOBALS._bCfgInvLocked) && bCanOpen) {
+ if (!GLOBALS._bCfgInvNoScroll) {
+ _state = OPENING;
+ _curPutY = RM_SY - 1;
+ _curPutTime = g_vm->getTime();
+ } else {
+ _state = OPENED;
+ _curPutY = RM_SY - 68;
+ }
+ } else if (_state == OPENED) {
+ if ((mpos._y < RM_SY - 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) {
+ if (!GLOBALS._bCfgInvNoScroll) {
+ _state = CLOSING;
+ _curPutY = RM_SY - 68;
+ _curPutTime = g_vm->getTime();
+ } else {
+ _state = CLOSED;
+ }
+ }
+ } else if (_state == OPENING) {
+ while (_curPutTime + INVSPEED < g_vm->getTime()) {
+ _curPutY -= 3;
+ _curPutTime += INVSPEED;
+ }
+
+ if (_curPutY <= RM_SY - 68) {
+ _state = OPENED;
+ _curPutY = RM_SY - 68;
+ }
+
+ } else if (_state == CLOSING) {
+ while (_curPutTime + INVSPEED < g_vm->getTime()) {
+ _curPutY += 3;
+ _curPutTime += INVSPEED;
+ }
+
+ if (_curPutY > 480)
+ _state = CLOSED;
+ }
+ } else {
+ if ((_state == CLOSED) && (mpos._y < 10 || GLOBALS._bCfgInvLocked) && bCanOpen) {
+ if (!GLOBALS._bCfgInvNoScroll) {
+ _state = OPENING;
+ _curPutY = - 68;
+ _curPutTime = g_vm->getTime();
+ } else {
+ _state = OPENED;
+ _curPutY = 0;
+ }
+ } else if (_state == OPENED) {
+ if ((mpos._y > 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) {
+ if (!GLOBALS._bCfgInvNoScroll) {
+ _state = CLOSING;
+ _curPutY = -2;
+ _curPutTime = g_vm->getTime();
+ } else {
+ _state = CLOSED;
+ }
+ }
+ } else if (_state == OPENING) {
+ while (_curPutTime + INVSPEED < g_vm->getTime()) {
+ _curPutY += 3;
+ _curPutTime += INVSPEED;
+ }
+
+ if (_curPutY >= 0) {
+ _state = OPENED;
+ _curPutY = 0;
+ }
+ } else if (_state == CLOSING) {
+ while (_curPutTime + INVSPEED < g_vm->getTime()) {
+ _curPutY -= 3;
+ _curPutTime += INVSPEED;
+ }
+
+ if (_curPutY < -68)
+ _state = CLOSED;
+ }
+ }
+
+ if (_state == SELECTING) {
+ int startx = (_nSelectObj + 1) * 64 - 20;
+ int starty;
+
+ if (!GLOBALS._bCfgInvUp)
+ starty = RM_SY - 109;
+ else
+ starty = 70;
+
+ // Make sure it is on one of the verbs
+ if (mpos._y > starty && mpos._y < starty + 45) {
+ if (mpos._x > startx && mpos._x < startx + 40) {
+ if (_miniAction != 1) {
+ _miniInterface.setPattern(2);
+ _miniAction = 1;
+ g_vm->playUtilSFX(1);
+ }
+ } else if (mpos._x >= startx + 40 && mpos._x < startx + 80) {
+ if (_miniAction != 2) {
+ _miniInterface.setPattern(3);
+ _miniAction = 2;
+ g_vm->playUtilSFX(1);
+ }
+ } else if (mpos._x >= startx + 80 && mpos._x < startx + 108) {
+ if (_miniAction != 3) {
+ _miniInterface.setPattern(4);
+ _miniAction = 3;
+ g_vm->playUtilSFX(1);
+ }
+ } else {
+ _miniInterface.setPattern(1);
+ _miniAction = 0;
+ }
+ } else {
+ _miniInterface.setPattern(1);
+ _miniAction = 0;
+ }
+
+ // Update the mini-interface
+ _miniInterface.doFrame(&bigBuf, false);
+ }
+
+ if ((_state != CLOSED) && !_nInList) {
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+ }
+}
+
+bool RMInventory::itemInFocus(const RMPoint &mpt) {
+ if ((_state == OPENED || _state == OPENING) && checkPointInside(mpt))
+ return true;
+ else
+ return false;
+}
+
+RMItem *RMInventory::whichItemIsIn(const RMPoint &mpt) {
+ if (_state == OPENED) {
+ if (checkPointInside(mpt)) {
+ int n = mpt._x / 64;
+ if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0 && (!_bCombining || _inv[n - 1 + _curPos] != _nCombine))
+ return &_items[_inv[n - 1 + _curPos]]._icon;
+ }
+ }
+
+ return NULL;
+}
+
+int RMInventory::getSaveStateSize() {
+ // m_inv pattern m_nInv
+ return 256 * 4 + 256 * 4 + 4;
+}
+
+void RMInventory::saveState(byte *state) {
+ WRITE_LE_UINT32(state, _nInv);
+ state += 4;
+ Common::copy(_inv, _inv + 256, (uint32 *)state);
+ state += 256 * 4;
+
+ int x;
+ for (int i = 0; i < 256; i++) {
+ if (i < _nItems)
+ x = _items[i]._status;
+ else
+ x = 0;
+
+ WRITE_LE_UINT32(state, x);
+ state += 4;
+ }
+}
+
+int RMInventory::loadState(byte *state) {
+ _nInv = READ_LE_UINT32(state);
+ state += 4;
+ Common::copy((uint32 *)state, (uint32 *)state + 256, _inv);
+ state += 256 * 4;
+
+ int x;
+ for (int i = 0; i < 256; i++) {
+ x = READ_LE_UINT32(state);
+ state += 4;
+
+ if (i < _nItems) {
+ _items[i]._status = x;
+ _items[i]._icon.setPattern(x);
+ }
+ }
+
+ _curPos = 0;
+ _bCombining = false;
+
+ _items[29]._icon.setPattern(1);
+
+ if (_nInv > 8)
+ _items[28]._icon.setPattern(2);
+ else
+ _items[28]._icon.setPattern(1);
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+
+ return getSaveStateSize();
+}
+
+RMInventory &RMInventory::operator+=(RMItem *item) {
+ addItem(item->mpalCode());
+ return *this;
+}
+
+RMInventory &RMInventory::operator+=(RMItem &item) {
+ addItem(item.mpalCode());
+ return *this;
+}
+
+RMInventory &RMInventory::operator+=(int code) {
+ addItem(code);
+ return *this;
+}
+
+/****************************************************************************\
+* RMInterface methods
+\****************************************************************************/
+
+RMInterface::RMInterface() : RMGfxSourceBuffer8RLEByte() {
+ _bActive = _bPerorate = false;
+ _lastHotZone = -1;
+}
+
+RMInterface::~RMInterface() {
+}
+
+bool RMInterface::active() {
+ return _bActive;
+}
+
+int RMInterface::onWhichBox(RMPoint pt) {
+ pt -= _openStart;
+
+ // Check how many verbs you have to consider
+ int max = 4;
+ if (_bPerorate)
+ max = 5;
+
+ // Find the verb
+ for (int i = 0; i < max; i++) {
+ if (_hotbbox[i].ptInRect(pt))
+ return i;
+ }
+
+ // Found no verb
+ return -1;
+}
+
+void RMInterface::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ int h;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ prim->getDst().topLeft() = _openStart;
+ CORO_INVOKE_2(RMGfxSourceBuffer8RLEByte::draw, bigBuf, prim);
+
+ // Check if there is a draw hot zone
+ _ctx->h = onWhichBox(_mpos);
+ if (_ctx->h != -1) {
+ prim->getDst().topLeft() = _openStart;
+ CORO_INVOKE_2(_hotzone[_ctx->h].draw, bigBuf, prim);
+
+ if (_lastHotZone != _ctx->h) {
+ _lastHotZone = _ctx->h;
+ g_vm->playUtilSFX(1);
+ }
+
+ if (GLOBALS._bCfgInterTips) {
+ prim->getDst().topLeft() = _openStart + RMPoint(70, 177);
+ CORO_INVOKE_2(_hints[_ctx->h].draw, bigBuf, prim);
+ }
+ } else
+ _lastHotZone = -1;
+
+ CORO_END_CODE;
+}
+
+void RMInterface::doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos) {
+ // If needed, add to the OT schedule list
+ if (!_nInList && _bActive)
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ _mpos = mousepos;
+}
+
+void RMInterface::clicked(const RMPoint &mousepos) {
+ _bActive = true;
+ _openPos = mousepos;
+
+ // Calculate the top left corner of the interface
+ _openStart = _openPos - RMPoint(_dimx / 2, _dimy / 2);
+ _lastHotZone = -1;
+
+ // Keep it inside the screen
+ if (_openStart._x < 0)
+ _openStart._x = 0;
+ if (_openStart._y < 0)
+ _openStart._y = 0;
+ if (_openStart._x + _dimx > RM_SX)
+ _openStart._x = RM_SX - _dimx;
+ if (_openStart._y + _dimy > RM_SY)
+ _openStart._y = RM_SY - _dimy;
+
+ // Play the sound effect
+ g_vm->playUtilSFX(0);
+}
+
+bool RMInterface::released(const RMPoint &mousepos, RMTonyAction &action) {
+ if (!_bActive)
+ return false;
+
+ _bActive = false;
+
+ switch (onWhichBox(mousepos)) {
+ case 0:
+ action = TA_TAKE;
+ break;
+
+ case 1:
+ action = TA_TALK;
+ break;
+
+ case 2:
+ action = TA_USE;
+ break;
+
+ case 3:
+ action = TA_EXAMINE;
+ break;
+
+ case 4:
+ action = TA_PERORATE;
+ break;
+
+ default: // No verb
+ return false;
+ }
+
+ return true;
+}
+
+void RMInterface::reset() {
+ _bActive = false;
+}
+
+void RMInterface::setPerorate(bool bOn) {
+ _bPerorate = bOn;
+}
+
+bool RMInterface::getPerorate() {
+ return _bPerorate;
+}
+
+void RMInterface::init() {
+ RMResRaw inter(RES_I_INTERFACE);
+ RMRes pal(RES_I_INTERPPAL);
+
+ setPriority(191);
+
+ RMGfxSourceBuffer::init(inter, inter.width(), inter.height());
+ loadPaletteWA(RES_I_INTERPAL);
+
+ for (int i = 0; i < 5; i++) {
+ RMResRaw part(RES_I_INTERP1 + i);
+
+ _hotzone[i].init(part, part.width(), part.height());
+ _hotzone[i].loadPaletteWA(pal);
+ }
+
+ _hotbbox[0].setRect(126, 123, 159, 208); // Take
+ _hotbbox[1].setRect(90, 130, 125, 186); // About
+ _hotbbox[2].setRect(110, 60, 152, 125);
+ _hotbbox[3].setRect(56, 51, 93, 99);
+ _hotbbox[4].setRect(51, 105, 82, 172);
+
+ _hints[0].setAlignType(RMText::HRIGHT, RMText::VTOP);
+ _hints[1].setAlignType(RMText::HRIGHT, RMText::VTOP);
+ _hints[2].setAlignType(RMText::HRIGHT, RMText::VTOP);
+ _hints[3].setAlignType(RMText::HRIGHT, RMText::VTOP);
+ _hints[4].setAlignType(RMText::HRIGHT, RMText::VTOP);
+
+ // The text is taken from MPAL for translation
+ RMMessage msg0(12);
+ RMMessage msg1(13);
+ RMMessage msg2(14);
+ RMMessage msg3(15);
+ RMMessage msg4(16);
+
+ _hints[0].writeText(msg0[0], 1); // Take
+ _hints[1].writeText(msg1[0], 1); // Talk
+ _hints[2].writeText(msg2[0], 1); // Use
+ _hints[3].writeText(msg3[0], 1); // Examine
+ _hints[4].writeText(msg4[0], 1); // Show Yourself
+
+ _bActive = false;
+ _bPerorate = false;
+ _lastHotZone = 0;
+}
+
+void RMInterface::close() {
+ destroy();
+
+ for (int i = 0; i < 5; i++)
+ _hotzone[i].destroy();
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/inventory.h b/engines/tony/inventory.h
new file mode 100644
index 0000000000..ce94c86c1b
--- /dev/null
+++ b/engines/tony/inventory.h
@@ -0,0 +1,241 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_INVENTORY_H
+#define TONY_INVENTORY_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "tony/font.h"
+#include "tony/game.h"
+#include "tony/gfxcore.h"
+#include "tony/loc.h"
+
+namespace Tony {
+
+struct RMInventoryItem {
+ RMItem _icon;
+ RMGfxSourceBuffer8RLEByteAA *_pointer;
+ int _status;
+};
+
+class RMInventory : public RMGfxWoodyBuffer {
+private:
+ enum InventoryState {
+ CLOSED,
+ OPENING,
+ OPENED,
+ CLOSING,
+ SELECTING
+ };
+
+protected:
+ int _nItems;
+ RMInventoryItem *_items;
+
+ int _inv[256];
+ int _nInv;
+ int _curPutY;
+ uint32 _curPutTime;
+
+ int _curPos;
+ InventoryState _state;
+ bool _bHasFocus;
+ int _nSelectObj;
+ int _nCombine;
+ bool _bCombining;
+
+ bool _bBlinkingRight, _bBlinkingLeft;
+
+ int _miniAction;
+ RMItem _miniInterface;
+ RMText _hints[3];
+
+ OSystem::MutexRef _csModifyInterface;
+
+protected:
+ /**
+ * Prepare the image inventory. It should be recalled whenever the inventory changes
+ */
+ void prepare();
+
+ /**
+ * Check if the mouse Y position is conrrect, even under the inventory portion of the screen
+ */
+ bool checkPointInside(const RMPoint &pt);
+
+public:
+ RMInventory();
+ virtual ~RMInventory();
+
+ /**
+ * Prepare a frame
+ */
+ void doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen);
+
+ /**
+ * Initialization and closing
+ */
+ void init();
+ void close();
+ void reset();
+
+ /**
+ * Overload test for removal from OT list
+ */
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ /**
+ * Overload the drawing of the inventory
+ */
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ /**
+ * Method for determining whether the inventory currently has the focus
+ */
+ bool haveFocus(const RMPoint &mpos);
+
+ /**
+ * Method for determining if the mini interface is active
+ */
+ bool miniActive();
+
+ /**
+ * Handle the left mouse click (only when the inventory has the focus)
+ */
+ bool leftClick(const RMPoint &mpos, int &nCombineObj);
+
+ /**
+ * Handle the right mouse button (only when the inventory has the focus)
+ */
+ void rightClick(const RMPoint &mpos);
+ bool rightRelease(const RMPoint &mpos, RMTonyAction &curAction);
+
+ /**
+ * Warn that an item combine is over
+ */
+ void endCombine();
+
+public:
+ /**
+ * Add an item to the inventory
+ */
+ void addItem(int code);
+ RMInventory &operator+=(RMItem *item);
+ RMInventory &operator+=(RMItem &item);
+ RMInventory &operator+=(int code);
+
+ /**
+ * Removes an item
+ */
+ void removeItem(int code);
+
+ /**
+ * We are on an object?
+ */
+ RMItem *whichItemIsIn(const RMPoint &mpt);
+ bool itemInFocus(const RMPoint &mpt);
+
+ /**
+ * Change the icon of an item
+ */
+ void changeItemStatus(uint32 dwCode, uint32 dwStatus);
+
+ /**
+ * Save methods
+ */
+ int getSaveStateSize();
+ void saveState(byte *state);
+ int loadState(byte *state);
+};
+
+
+class RMInterface : public RMGfxSourceBuffer8RLEByte {
+private:
+ bool _bActive;
+ RMPoint _mpos;
+ RMPoint _openPos;
+ RMPoint _openStart;
+ RMText _hints[5];
+ RMGfxSourceBuffer8RLEByte _hotzone[5];
+ RMRect _hotbbox[5];
+ bool _bPerorate;
+ int _lastHotZone;
+
+protected:
+ /**
+ * Return which box a given point is in
+ */
+ int onWhichBox(RMPoint pt);
+
+public:
+ RMInterface();
+ virtual ~RMInterface();
+
+ /**
+ * The usual DoFrame (poll the graphics engine)
+ */
+ void doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos);
+
+ /**
+ * TRUE if it is active (you can select items)
+ */
+ bool active();
+
+ /**
+ * Initialization
+ */
+ void init();
+ void close();
+
+ /**
+ * Reset the interface
+ */
+ void reset();
+
+ /**
+ * Warns of mouse clicks and releases
+ */
+ void clicked(const RMPoint &mousepos);
+ bool released(const RMPoint &mousepos, RMTonyAction &action);
+
+ /**
+ * Enables or disables the fifth verb
+ */
+ void setPerorate(bool bOn);
+ bool getPerorate();
+
+ /**
+ * Overloaded Draw
+ */
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/loc.cpp b/engines/tony/loc.cpp
new file mode 100644
index 0000000000..18470aa6fc
--- /dev/null
+++ b/engines/tony/loc.cpp
@@ -0,0 +1,2280 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/memstream.h"
+#include "common/scummsys.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/game.h"
+#include "tony/loc.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+using namespace ::Tony::MPAL;
+
+
+/****************************************************************************\
+* RMPalette Methods
+\****************************************************************************/
+
+void RMPalette::readFromStream(Common::ReadStream &ds) {
+ ds.read(_data, 1024);
+}
+
+/****************************************************************************\
+* RMSlot Methods
+\****************************************************************************/
+
+void RMPattern::RMSlot::readFromStream(Common::ReadStream &ds, bool bLOX) {
+ // Type
+ byte type = ds.readByte();
+ _type = (RMPattern::RMSlotType)type;
+
+ // Data
+ _data = ds.readSint32LE();
+
+ // Position
+ _pos.readFromStream(ds);
+
+ // Generic flag
+ _flag = ds.readByte();
+}
+
+
+/****************************************************************************\
+* RMPattern Methods
+\****************************************************************************/
+
+void RMPattern::readFromStream(Common::ReadStream &ds, bool bLOX) {
+ // Pattern name
+ if (!bLOX)
+ _name = readString(ds);
+
+ // Velocity
+ _speed = ds.readSint32LE();
+
+ // Position
+ _pos.readFromStream(ds);
+
+ // Flag for pattern looping
+ _bLoop = ds.readSint32LE();
+
+ // Number of slots
+ _nSlots = ds.readSint32LE();
+
+ // Create and read the slots
+ _slots = new RMSlot[_nSlots];
+
+ for (int i = 0; i < _nSlots && !ds.err(); i++) {
+ if (bLOX)
+ _slots[i].readFromStream(ds, true);
+ else
+ _slots[i].readFromStream(ds, false);
+ }
+}
+
+void RMPattern::updateCoord() {
+ _curPos = _pos + _slots[_nCurSlot].pos();
+}
+
+void RMPattern::stopSfx(RMSfx *sfx) {
+ for (int i = 0; i < _nSlots; i++) {
+ if (_slots[i]._type == SOUND) {
+ if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_')
+ sfx[_slots[i]._data].stop();
+ else if (GLOBALS._bSkipSfxNoLoop)
+ sfx[_slots[i]._data].stop();
+ }
+ }
+}
+
+int RMPattern::init(RMSfx *sfx, bool bPlayP0, byte *bFlag) {
+ // Read the current time
+ _nStartTime = g_vm->getTime();
+ _nCurSlot = 0;
+
+ // Find the first frame of the pattern
+ int i = 0;
+ while (_slots[i]._type != SPRITE) {
+ assert(i + 1 < _nSlots);
+ i++;
+ }
+
+ _nCurSlot = i;
+ _nCurSprite = _slots[i]._data;
+ if (bFlag)
+ *bFlag = _slots[i]._flag;
+
+ // Calculate the current coordinates
+ updateCoord();
+
+ // Check for sound:
+ // If the slot is 0, play
+ // If speed == 0, must play unless it goes into loop '_', or if specified by the parameter
+ // If speed != 0, play only the loop
+ for (i = 0; i < _nSlots; i++) {
+ if (_slots[i]._type == SOUND) {
+ if (i == 0) {
+ if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play(true);
+ } else {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play();
+ }
+ } else if (_speed == 0) {
+ if (bPlayP0) {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play();
+ } else if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play(true);
+ }
+ } else {
+ if (_bLoop && !sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play(true);
+ }
+ }
+ }
+ }
+
+ return _nCurSprite;
+}
+
+int RMPattern::update(uint32 hEndPattern, byte &bFlag, RMSfx *sfx) {
+ int CurTime = g_vm->getTime();
+
+ // If the speed is 0, then the pattern never advances
+ if (_speed == 0) {
+ CoroScheduler.pulseEvent(hEndPattern);
+ bFlag = _slots[_nCurSlot]._flag;
+ return _nCurSprite;
+ }
+
+ // Is it time to change the slots?
+ while (_nStartTime + _speed <= (uint32)CurTime) {
+ _nStartTime += _speed;
+ if (_slots[_nCurSlot]._type == SPRITE)
+ _nCurSlot++;
+ if (_nCurSlot == _nSlots) {
+ _nCurSlot = 0;
+ bFlag = _slots[_nCurSlot]._flag;
+
+ CoroScheduler.pulseEvent(hEndPattern);
+
+ // @@@ If there is no loop pattern, and there's a warning that it's the final
+ // frame, then remain on the last frame
+ if (!_bLoop) {
+ _nCurSlot = _nSlots - 1;
+ bFlag = _slots[_nCurSlot]._flag;
+ return _nCurSprite;
+ }
+ }
+
+ for (;;) {
+ switch (_slots[_nCurSlot]._type) {
+ case SPRITE:
+ // Read the next sprite
+ _nCurSprite = _slots[_nCurSlot]._data;
+
+ // Update the parent & child coordinates
+ updateCoord();
+ break;
+
+ case SOUND:
+ if (sfx != NULL) {
+ sfx[_slots[_nCurSlot]._data].setVolume(_slots[_nCurSlot].pos()._x);
+
+ if (sfx[_slots[_nCurSlot]._data]._name.empty() || sfx[_slots[_nCurSlot]._data]._name[0] != '_')
+ sfx[_slots[_nCurSlot]._data].play(false);
+ else
+ sfx[_slots[_nCurSlot]._data].play(true);
+ }
+ break;
+
+ case COMMAND:
+ assert(0);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ if (_slots[_nCurSlot]._type == SPRITE)
+ break;
+ _nCurSlot++;
+ }
+ }
+
+ // Return the current sprite
+ bFlag = _slots[_nCurSlot]._flag;
+ return _nCurSprite;
+}
+
+RMPattern::RMPattern() {
+ _slots = NULL;
+ _speed = 0;
+ _bLoop = 0;
+ _nSlots = 0;
+ _nCurSlot = 0;
+ _nCurSprite = 0;
+ _nStartTime = 0;
+ _slots = NULL;
+}
+
+/**
+ * Reads the position of the pattern
+ */
+RMPoint RMPattern::pos() {
+ return _curPos;
+}
+
+RMPattern::~RMPattern() {
+ if (_slots != NULL) {
+ delete[] _slots;
+ _slots = NULL;
+ }
+}
+
+/****************************************************************************\
+* RMSprite Methods
+\****************************************************************************/
+
+void RMSprite::init(RMGfxSourceBuffer *buf) {
+ _buf = buf;
+}
+
+void RMSprite::LOXGetSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy) {
+ uint32 pos = ds.pos();
+
+ *dimx = ds.readSint32LE();
+ *dimy = ds.readSint32LE();
+
+ ds.seek(pos);
+}
+
+void RMSprite::getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy) {
+ uint32 pos = ds.pos();
+
+ _name = readString(ds);
+ *dimx = ds.readSint32LE();
+ *dimy = ds.readSint32LE();
+
+ ds.seek(pos);
+}
+
+void RMSprite::readFromStream(Common::SeekableReadStream &ds, bool bLOX) {
+ // Sprite name
+ if (!bLOX)
+ _name = readString(ds);
+
+ // Dimensions
+ int dimx = ds.readSint32LE();
+ int dimy = ds.readSint32LE();
+
+ // Bounding box
+ _rcBox.readFromStream(ds);
+
+ // Unused space
+ if (!bLOX)
+ ds.skip(32);
+
+ // Create buffer and read
+ _buf->init(ds, dimx, dimy);
+}
+
+void RMSprite::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ _buf->draw(coroParam, bigBuf, prim);
+}
+
+void RMSprite::setPalette(byte *buf) {
+ ((RMGfxSourceBufferPal *)_buf)->loadPalette(buf);
+}
+
+RMSprite::RMSprite() {
+ _buf = NULL;
+}
+
+RMSprite::~RMSprite() {
+ if (_buf) {
+ delete _buf;
+ _buf = NULL;
+ }
+}
+
+
+/****************************************************************************\
+* RMSfx Methods
+\****************************************************************************/
+
+void RMSfx::readFromStream(Common::ReadStream &ds, bool bLOX) {
+ // sfx name
+ _name = readString(ds);
+
+ int size = ds.readSint32LE();
+
+ // Read the entire buffer into a MemoryReadStream
+ byte *buffer = (byte *)malloc(size);
+ ds.read(buffer, size);
+ Common::SeekableReadStream *stream = new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
+
+ // Create the sound effect
+ _fx = g_vm->createSFX(stream);
+ _fx->setLoop(false);
+}
+
+RMSfx::RMSfx() {
+ _fx = NULL;
+ _bPlayingLoop = false;
+}
+
+RMSfx::~RMSfx() {
+ if (_fx) {
+ _fx->release();
+ _fx = NULL;
+ }
+}
+
+void RMSfx::play(bool bLoop) {
+ if (_fx && !_bPlayingLoop) {
+ _fx->setLoop(bLoop);
+ _fx->play();
+
+ if (bLoop)
+ _bPlayingLoop = true;
+ }
+}
+
+void RMSfx::setVolume(int vol) {
+ if (_fx) {
+ _fx->setVolume(vol);
+ }
+}
+
+void RMSfx::pause(bool bPause) {
+ if (_fx) {
+ _fx->setPause(bPause);
+ }
+}
+
+void RMSfx::stop() {
+ if (_fx) {
+ _fx->stop();
+ _bPlayingLoop = false;
+ }
+}
+
+
+
+/****************************************************************************\
+* RMItem Methods
+\****************************************************************************/
+
+int RMItem::getCurPattern() {
+ return _nCurPattern;
+}
+
+RMGfxSourceBuffer *RMItem::newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE) {
+ if (_cm == CM_256) {
+ RMGfxSourceBuffer8RLE *spr;
+
+ if (_FX == 2) { // AB
+ spr = new RMGfxSourceBuffer8RLEWordAB;
+ } else if (_FX == 1) { // OMBRA+AA
+ if (dimx == -1 || dimx > 255)
+ spr = new RMGfxSourceBuffer8RLEWordAA;
+ else
+ spr = new RMGfxSourceBuffer8RLEByteAA;
+
+ spr->setAlphaBlendColor(_FXparm);
+ if (bPreRLE)
+ spr->setAlreadyCompressed();
+ } else {
+ if (dimx == -1 || dimx > 255)
+ spr = new RMGfxSourceBuffer8RLEWord;
+ else
+ spr = new RMGfxSourceBuffer8RLEByte;
+
+ if (bPreRLE)
+ spr->setAlreadyCompressed();
+ }
+
+ return spr;
+ } else
+ return new RMGfxSourceBuffer16;
+}
+
+bool RMItem::isIn(const RMPoint &pt, int *size) {
+ RMRect rc;
+
+ if (!_bIsActive)
+ return false;
+
+ // Search for the right bounding box to use - use the sprite's if it has one, otherwise use the generic one
+ if (_nCurPattern != 0 && !_sprites[_nCurSprite]._rcBox.isEmpty())
+ rc = _sprites[_nCurSprite]._rcBox + calculatePos();
+ else if (!_rcBox.isEmpty())
+ rc = _rcBox;
+ // If no box, return immediately
+ else
+ return false;
+
+ if (size != NULL)
+ *size = rc.size();
+
+ return rc.ptInRect(pt + _curScroll);
+}
+
+void RMItem::readFromStream(Common::SeekableReadStream &ds, bool bLOX) {
+ // MPAL code
+ _mpalCode = ds.readSint32LE();
+
+ // Object name
+ _name = readString(ds);
+
+ // Z (signed)
+ _z = ds.readSint32LE();
+
+ // Parent position
+ _pos.readFromStream(ds);
+
+ // Hotspot
+ _hot.readFromStream(ds);
+
+ // Bounding box
+ _rcBox.readFromStream(ds);
+
+ // Number of sprites, sound effects, and patterns
+ _nSprites = ds.readSint32LE();
+ _nSfx = ds.readSint32LE();
+ _nPatterns = ds.readSint32LE();
+
+ // Color mode
+ byte cm = ds.readByte();
+ _cm = (RMColorMode)cm;
+
+ // Flag for the presence of custom palette differences
+ _bPal = ds.readByte();
+
+ if (_cm == CM_256) {
+ // If there is a palette, read it in
+ if (_bPal)
+ _pal.readFromStream(ds);
+ }
+
+ // MPAL data
+ if (!bLOX)
+ ds.skip(20);
+
+ _FX = ds.readByte();
+ _FXparm = ds.readByte();
+
+ if (!bLOX)
+ ds.skip(106);
+
+ // Create sub-classes
+ if (_nSprites > 0)
+ _sprites = new RMSprite[_nSprites];
+ if (_nSfx > 0)
+ _sfx = new RMSfx[_nSfx];
+ _patterns = new RMPattern[_nPatterns + 1];
+
+ int dimx, dimy;
+ // Read in class data
+ if (!ds.err()) {
+ for (int i = 0; i < _nSprites && !ds.err(); i++) {
+ // Download the sprites
+ if (bLOX) {
+ _sprites[i].LOXGetSizeFromStream(ds, &dimx, &dimy);
+ _sprites[i].init(newItemSpriteBuffer(dimx, dimy, true));
+ _sprites[i].readFromStream(ds, true);
+ } else {
+ _sprites[i].getSizeFromStream(ds, &dimx, &dimy);
+ _sprites[i].init(newItemSpriteBuffer(dimx, dimy, false));
+ _sprites[i].readFromStream(ds, false);
+ }
+
+ if (_cm == CM_256 && _bPal)
+ _sprites[i].setPalette(_pal._data);
+ }
+ }
+
+ if (!ds.err()) {
+ for (int i = 0; i < _nSfx && !ds.err(); i++) {
+ if (bLOX)
+ _sfx[i].readFromStream(ds, true);
+ else
+ _sfx[i].readFromStream(ds, false);
+ }
+ }
+
+ // Read the pattern from pattern 1
+ if (!ds.err()) {
+ for (int i = 1; i <= _nPatterns && !ds.err(); i++) {
+ if (bLOX)
+ _patterns[i].readFromStream(ds, true);
+ else
+ _patterns[i].readFromStream(ds, false);
+ }
+ }
+
+ // Initialize the current pattern
+ if (_bInitCurPattern)
+ setPattern(mpalQueryItemPattern(_mpalCode));
+
+ // Initialize the current activation state
+ _bIsActive = mpalQueryItemIsActive(_mpalCode);
+}
+
+
+RMGfxPrimitive *RMItem::newItemPrimitive() {
+ return new RMGfxPrimitive(this);
+}
+
+void RMItem::setScrollPosition(const RMPoint &scroll) {
+ _curScroll = scroll;
+}
+
+bool RMItem::doFrame(RMGfxTargetBuffer *bigBuf, bool bAddToList) {
+ int oldSprite = _nCurSprite;
+
+ // Pattern 0 = Do not draw anything!
+ if (_nCurPattern == 0)
+ return false;
+
+ // We do an update of the pattern, which also returns the current frame
+ if (_nCurPattern != 0) {
+ _nCurSprite = _patterns[_nCurPattern].update(_hEndPattern, _bCurFlag, _sfx);
+
+ // WORKAROUND: Currently, m_nCurSprite = -1 is used to flag that an item should be removed.
+ // However, this seems to be done inside a process waiting on an event pulsed inside the pattern
+ // Update method. So the value of m_nCurSprite = -1 is being destroyed with the return value
+ // replacing it. It may be that the current coroutine PulseEvent implementation is wrong somehow.
+ // In any case, a special check here is done for items that have ended
+ if (_nCurPattern == 0)
+ _nCurSprite = -1;
+ }
+
+ // If the function returned -1, it means that the pattern has finished
+ if (_nCurSprite == -1) {
+ // We have pattern 0, so leave. The class will self de-register from the OT list
+ _nCurPattern = 0;
+ return false;
+ }
+
+ // If we are not in the OT list, add ourselves
+ if (!_nInList && bAddToList)
+ bigBuf->addPrim(newItemPrimitive());
+
+ return oldSprite != _nCurSprite;
+}
+
+RMPoint RMItem::calculatePos() {
+ return _pos + _patterns[_nCurPattern].pos();
+}
+
+void RMItem::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // If CurSprite == -1, then the pattern is finished
+ if (_nCurSprite == -1)
+ return;
+
+ // Set the flag
+ prim->setFlag(_bCurFlag);
+
+ // Offset direction for scrolling
+ prim->getDst().offset(-_curScroll);
+
+ // We must offset the cordinates of the item inside the primitive
+ // It is estimated as nonno + (babbo + figlio)
+ prim->getDst().offset(calculatePos());
+
+ // No stretching, please
+ prim->setStretch(false);
+
+ // Now we turn to the generic surface drawing routines
+ CORO_INVOKE_2(_sprites[_nCurSprite].draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+/**
+ * Overloaded priority: it's based on Z ordering
+ */
+int RMItem::priority() {
+ return _z;
+}
+
+/**
+ * Pattern number
+ */
+int RMItem::numPattern() {
+ return _nPatterns;
+}
+
+void RMItem::removeThis(CORO_PARAM, bool &result) {
+ // Remove from the OT list if the current frame is -1 (pattern over)
+ result = (_nCurSprite == -1);
+}
+
+
+void RMItem::setStatus(int nStatus) {
+ _bIsActive = (nStatus > 0);
+}
+
+RMPoint RMItem::getHotspot() {
+ return _hot;
+}
+
+int RMItem::mpalCode() {
+ return _mpalCode;
+}
+
+void RMItem::setPattern(int nPattern, bool bPlayP0) {
+ assert(nPattern >= 0 && nPattern <= _nPatterns);
+
+ if (_sfx) {
+ if (_nCurPattern > 0)
+ _patterns[_nCurPattern].stopSfx(_sfx);
+ }
+
+ // Remember the current pattern
+ _nCurPattern = nPattern;
+
+ // Start the pattern to start the animation
+ if (_nCurPattern != 0)
+ _nCurSprite = _patterns[_nCurPattern].init(_sfx, bPlayP0, &_bCurFlag);
+ else {
+ _nCurSprite = -1;
+
+ // Look for the sound effect for pattern 0
+ if (bPlayP0) {
+ for (int i = 0; i < _nSfx; i++) {
+ if (_sfx[i]._name == "p0")
+ _sfx[i].play();
+ }
+ }
+ }
+}
+
+bool RMItem::getName(Common::String &name) {
+ char buf[256];
+
+ mpalQueryItemName(_mpalCode, buf);
+ name = buf;
+ if (buf[0] == '\0')
+ return false;
+ return true;
+}
+
+void RMItem::unload() {
+ if (_patterns != NULL) {
+ delete[] _patterns;
+ _patterns = NULL;
+ }
+
+ if (_sprites != NULL) {
+ delete[] _sprites;
+ _sprites = NULL;
+ }
+
+ if (_sfx != NULL) {
+ delete[] _sfx;
+ _sfx = NULL;
+ }
+}
+
+RMItem::RMItem() {
+ _bCurFlag = 0;
+ _patterns = NULL;
+ _sprites = NULL;
+ _sfx = NULL;
+ _curScroll.set(0, 0);
+ _bInitCurPattern = true;
+ _nCurPattern = 0;
+ _z = 0;
+ _cm = CM_256;
+ _FX = 0;
+ _FXparm = 0;
+ _mpalCode = 0;
+ _nSprites = 0;
+ _nSfx = 0;
+ _nPatterns = 0;
+ _bPal = 0;
+ _nCurSprite = 0;
+
+ _bIsActive = false;
+ memset(_pal._data, 0, sizeof(_pal._data));
+
+ _hEndPattern = CoroScheduler.createEvent(false, false);
+}
+
+RMItem::~RMItem() {
+ unload();
+ CoroScheduler.closeEvent(_hEndPattern);
+}
+
+
+void RMItem::waitForEndPattern(CORO_PARAM, uint32 hCustomSkip) {
+ CORO_BEGIN_CONTEXT;
+ uint32 h[2];
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_nCurPattern != 0) {
+ if (hCustomSkip == CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndPattern, CORO_INFINITE);
+ else {
+ _ctx->h[0] = hCustomSkip;
+ _ctx->h[1] = _hEndPattern;
+ CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, 2, &_ctx->h[0], false, CORO_INFINITE);
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMItem::changeHotspot(const RMPoint &pt) {
+ _hot = pt;
+}
+
+void RMItem::setInitCurPattern(bool status) {
+ _bInitCurPattern = status;
+}
+
+void RMItem::playSfx(int nSfx) {
+ if (nSfx < _nSfx)
+ _sfx[nSfx].play();
+}
+
+void RMItem::pauseSound(bool bPause) {
+ for (int i = 0; i < _nSfx; i++)
+ _sfx[i].pause(bPause);
+}
+
+
+/****************************************************************************\
+* RMWipe Methods
+\****************************************************************************/
+
+
+RMWipe::RMWipe() {
+ _hUnregistered = CoroScheduler.createEvent(false, false);
+ _hEndOfFade = CoroScheduler.createEvent(false, false);
+
+ _bMustRegister = false;
+ _bUnregister = false;
+ _bEndFade = false;
+ _bFading = false;
+ _nFadeStep = 0;
+
+}
+
+RMWipe::~RMWipe() {
+ CoroScheduler.closeEvent(_hUnregistered);
+ CoroScheduler.closeEvent(_hEndOfFade);
+}
+
+int RMWipe::priority() {
+ return 200;
+}
+
+void RMWipe::unregister() {
+ RMGfxTask::unregister();
+ assert(_nInList == 0);
+ CoroScheduler.setEvent(_hUnregistered);
+}
+
+void RMWipe::removeThis(CORO_PARAM, bool &result) {
+ result = _bUnregister;
+}
+
+void RMWipe::waitForFadeEnd(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndOfFade, CORO_INFINITE);
+
+ _bEndFade = true;
+ _bFading = false;
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+void RMWipe::closeFade() {
+ _wip0r.unload();
+}
+
+void RMWipe::initFade(int type) {
+ // Activate the fade
+ _bUnregister = false;
+ _bEndFade = false;
+
+ _nFadeStep = 0;
+
+ _bMustRegister = true;
+
+ RMRes res(RES_W_CIRCLE);
+ Common::SeekableReadStream *ds = res.getReadStream();
+ _wip0r.readFromStream(*ds);
+ delete ds;
+
+ _wip0r.setPattern(1);
+
+ _bFading = true;
+}
+
+void RMWipe::doFrame(RMGfxTargetBuffer &bigBuf) {
+ if (_bMustRegister) {
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+ _bMustRegister = false;
+ }
+
+ if (_bFading) {
+ _wip0r.doFrame(&bigBuf, false);
+
+ _nFadeStep++;
+
+ if (_nFadeStep == 10) {
+ CoroScheduler.setEvent(_hEndOfFade);
+ }
+ }
+}
+
+void RMWipe::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bFading) {
+ CORO_INVOKE_2(_wip0r.draw, bigBuf, prim);
+ }
+
+ if (_bEndFade)
+ Common::fill((byte *)bigBuf, (byte *)bigBuf + bigBuf.getDimx() * bigBuf.getDimy() * 2, 0x0);
+
+ CORO_END_CODE;
+}
+
+/****************************************************************************\
+* RMCharacter Methods
+\****************************************************************************/
+
+/****************************************************************************/
+/* Find the shortest path between two nodes of the graph connecting the BOX */
+/* Returns path along the vector path path[] */
+/****************************************************************************/
+
+short RMCharacter::findPath(short source, short destination) {
+ static RMBox box[MAXBOXES]; // Matrix of adjacent boxes
+ static short nodeCost[MAXBOXES]; // Cost per node
+ static short valid[MAXBOXES]; // 0:Invalid 1:Valid 2:Saturated
+ static short nextNode[MAXBOXES]; // Next node
+ short minCost, error = 0;
+ RMBoxLoc *cur;
+
+ g_system->lockMutex(_csMove);
+
+ if (source == -1 || destination == -1) {
+ g_system->unlockMutex(_csMove);
+ return 0;
+ }
+
+ // Get the boxes
+ cur = _theBoxes->getBoxes(_curLocation);
+
+ // Make a backup copy to work on
+ for (int i = 0; i < cur->_numbBox; i++)
+ memcpy(&box[i], &cur->_boxes[i], sizeof(RMBox));
+
+ // Invalidate all nodes
+ for (int i = 0; i < cur->_numbBox; i++)
+ valid[i] = 0;
+
+ // Prepare source and variables for the procedure
+ nodeCost[source] = 0;
+ valid[source] = 1;
+ bool finish = false;
+
+ // Find the shortest path
+ while (!finish) {
+ minCost = 32000; // Reset the minimum cost
+ error = 1; // Possible error
+
+ // 1st cycle: explore possible new nodes
+ for (int i = 0; i < cur->_numbBox; i++) {
+ if (valid[i] == 1) {
+ error = 0; // Failure de-bunked
+ int j = 0;
+ while (((box[i]._adj[j]) != 1) && (j < cur->_numbBox))
+ j++;
+
+ if (j >= cur->_numbBox)
+ valid[i] = 2; // nodo saturated?
+ else {
+ nextNode[i] = j;
+ if (nodeCost[i] + 1 < minCost)
+ minCost = nodeCost[i] + 1;
+ }
+ }
+ }
+
+ if (error)
+ finish = true; // All nodes saturated
+
+ // 2nd cycle: adding new nodes that were found, saturate old nodes
+ for (int i = 0; i < cur->_numbBox; i++) {
+ if ((valid[i] == 1) && ((nodeCost[i] + 1) == minCost)) {
+ box[i]._adj[nextNode[i]] = 2;
+ nodeCost[nextNode[i]] = minCost;
+ valid[nextNode[i]] = 1;
+ for (int j = 0; j < cur->_numbBox; j++)
+ if (box[j]._adj[nextNode[i]] == 1)
+ box[j]._adj[nextNode[i]] = 0;
+
+ if (nextNode[i] == destination)
+ finish = true;
+ }
+ }
+ }
+
+ // Remove the path from the adjacent modified matrixes
+ if (!error) {
+ _pathLength = nodeCost[destination];
+ short k = _pathLength;
+ _path[k] = destination;
+
+ while (_path[k] != source) {
+ int i = 0;
+ while (box[i]._adj[_path[k]] != 2)
+ i++;
+ k--;
+ _path[k] = i;
+ }
+
+ _pathLength++;
+ }
+
+ g_system->unlockMutex(_csMove);
+
+ return !error;
+}
+
+
+void RMCharacter::goTo(CORO_PARAM, RMPoint destcoord, bool bReversed) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_pos == destcoord) {
+ if (_minPath == 0) {
+ CORO_INVOKE_0(stop);
+ CoroScheduler.pulseEvent(_hEndOfPath);
+ return;
+ }
+ }
+
+ _status = WALK;
+ _lineStart = _pos;
+ _lineEnd = destcoord;
+ _dx = _lineStart._x - _lineEnd._x;
+ _dy = _lineStart._y - _lineEnd._y;
+ _fx = _dx;
+ _fy = _dy;
+ _dx = ABS(_dx);
+ _dy = ABS(_dy);
+ _walkSpeed = _curSpeed;
+ _walkCount = 0;
+
+ if (bReversed) {
+ while (0) ;
+ }
+
+ int nPatt = getCurPattern();
+
+ if (_dx > _dy) {
+ _slope = _fy / _fx;
+ if (_lineEnd._x < _lineStart._x)
+ _walkSpeed = -_walkSpeed;
+ _walkStatus = 1;
+
+ // Change the pattern for the new direction
+ _bNeedToStop = true;
+ if ((_walkSpeed < 0 && !bReversed) || (_walkSpeed >= 0 && bReversed)) {
+ if (nPatt != PAT_WALKLEFT)
+ setPattern(PAT_WALKLEFT);
+ } else {
+ if (nPatt != PAT_WALKRIGHT)
+ setPattern(PAT_WALKRIGHT);
+ }
+ } else {
+ _slope = _fx / _fy;
+ if (_lineEnd._y < _lineStart._y)
+ _walkSpeed = -_walkSpeed;
+ _walkStatus = 0;
+
+ _bNeedToStop = true;
+ if ((_walkSpeed < 0 && !bReversed) || (_walkSpeed >= 0 && bReversed)) {
+ if (nPatt != PAT_WALKUP)
+ setPattern(PAT_WALKUP);
+ } else {
+ if (nPatt != PAT_WALKDOWN)
+ setPattern(PAT_WALKDOWN);
+ }
+ }
+
+ _olddx = _dx;
+ _olddy = _dy;
+
+ CORO_END_CODE;
+}
+
+
+RMPoint RMCharacter::searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint point) {
+ short steps;
+ RMPoint newPt, foundPt;
+ short minStep = 32000;
+
+ if (UP) {
+ newPt = point;
+ steps = 0;
+ while ((inWhichBox(newPt) == -1) && (newPt._y >= 0)) {
+ newPt._y--;
+ steps++;
+ }
+ if ((inWhichBox(newPt) != -1) && (steps < minStep) &&
+ findPath(inWhichBox(_pos), inWhichBox(newPt))) {
+ minStep = steps;
+ newPt._y--; // to avoid error?
+ foundPt = newPt;
+ }
+ }
+
+ if (DOWN) {
+ newPt = point;
+ steps = 0;
+ while ((inWhichBox(newPt) == -1) && (newPt._y < 480)) {
+ newPt._y++;
+ steps++;
+ }
+ if ((inWhichBox(newPt) != -1) && (steps < minStep) &&
+ findPath(inWhichBox(_pos), inWhichBox(newPt))) {
+ minStep = steps;
+ newPt._y++; // to avoid error?
+ foundPt = newPt;
+ }
+ }
+
+ if (RIGHT) {
+ newPt = point;
+ steps = 0;
+ while ((inWhichBox(newPt) == -1) && (newPt._x < 640)) {
+ newPt._x++;
+ steps++;
+ }
+ if ((inWhichBox(newPt) != -1) && (steps < minStep) &&
+ findPath(inWhichBox(_pos), inWhichBox(newPt))) {
+ minStep = steps;
+ newPt._x++; // to avoid error?
+ foundPt = newPt;
+ }
+ }
+
+ if (LEFT) {
+ newPt = point;
+ steps = 0;
+ while ((inWhichBox(newPt) == -1) && (newPt._x >= 0)) {
+ newPt._x--;
+ steps++;
+ }
+ if ((inWhichBox(newPt) != -1) && (steps < minStep) &&
+ findPath(inWhichBox(_pos), inWhichBox(newPt))) {
+ minStep = steps;
+ newPt._x--; // to avoid error?
+ foundPt = newPt;
+ }
+ }
+
+ if (minStep == 32000)
+ foundPt = point;
+
+ return foundPt;
+}
+
+
+RMPoint RMCharacter::nearestPoint(const RMPoint &point) {
+ return searching(1, 1, 1, 1, point);
+}
+
+
+short RMCharacter::scanLine(const RMPoint &point) {
+ int Ldx, Ldy, Lcount;
+ float Lfx, Lfy, Lslope;
+ RMPoint Lstart, Lend, Lscan;
+ signed char Lspeed, Lstatus;
+
+ Lstart = _pos;
+ Lend = point;
+ Ldx = Lstart._x - Lend._x;
+ Ldy = Lstart._y - Lend._y;
+ Lfx = Ldx;
+ Lfy = Ldy;
+ Ldx = ABS(Ldx);
+ Ldy = ABS(Ldy);
+ Lspeed = 1;
+ Lcount = 0;
+
+ if (Ldx > Ldy) {
+ Lslope = Lfy / Lfx;
+ if (Lend._x < Lstart._x)
+ Lspeed = -Lspeed;
+ Lstatus = 1;
+ } else {
+ Lslope = Lfx / Lfy;
+ if (Lend._y < Lstart._y)
+ Lspeed = - Lspeed;
+ Lstatus = 0;
+ }
+
+ Lscan = Lstart; // Start scanning
+ while (inWhichBox(Lscan) != -1) {
+ Lcount++;
+ if (Lstatus) {
+ Ldx = Lspeed * Lcount;
+ Ldy = (int)(Lslope * Ldx);
+ } else {
+ Ldy = Lspeed * Lcount;
+ Ldx = (int)(Lslope * Ldy);
+ }
+
+ Lscan._x = Lstart._x + Ldx;
+ Lscan._y = Lstart._y + Ldy;
+
+ if ((ABS(Lscan._x - Lend._x) <= 1) && (ABS(Lscan._y - Lend._y) <= 1))
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Calculates intersections between the straight line and the closest BBOX
+ */
+RMPoint RMCharacter::invScanLine(const RMPoint &point) {
+ RMPoint lStart = point; // Exchange!
+ RMPoint lEnd = _pos; // :-)
+ int lDx = lStart._x - lEnd._x;
+ int lDy = lStart._y - lEnd._y;
+ float lFx = lDx;
+ float lFy = lDy;
+ lDx = ABS(lDx);
+ lDy = ABS(lDy);
+ signed char lSpeed = 1;
+ int lCount = 0;
+
+ signed char lStatus;
+ float lSlope;
+
+ if (lDx > lDy) {
+ lSlope = lFy / lFx;
+ if (lEnd._x < lStart._x)
+ lSpeed = -lSpeed;
+ lStatus = 1;
+ } else {
+ lSlope = lFx / lFy;
+ if (lEnd._y < lStart._y)
+ lSpeed = -lSpeed;
+ lStatus = 0;
+ }
+
+ RMPoint lScan = lStart;
+ signed char lBox = -1;
+
+ for (;;) {
+ if (inWhichBox(lScan) != -1) {
+ if (inWhichBox(lScan) != lBox) {
+ if (inWhichBox(_pos) == inWhichBox(lScan) || findPath(inWhichBox(_pos), inWhichBox(lScan)))
+ return lScan;
+ else
+ lBox = inWhichBox(lScan);
+ }
+ }
+
+ lCount++;
+ if (lStatus) {
+ lDx = lSpeed * lCount;
+ lDy = (int)(lSlope * lDx);
+ } else {
+ lDy = lSpeed * lCount;
+ lDx = (int)(lSlope * lDy);
+ }
+ lScan._x = lStart._x + lDx;
+ lScan._y = lStart._y + lDy;
+
+ // WORKAROUND: Handles cases where the points never fall inside a bounding box
+ if (lScan._x < -100 || lScan._y < -100 || lScan._x >= 1000 || lScan._y >= 1000)
+ return point;
+ }
+}
+
+
+/**
+ * Returns the HotSpot coordinate closest to the player
+ */
+
+RMPoint RMCharacter::nearestHotSpot(int sourcebox, int destbox) {
+ RMPoint hotspot;
+ int x, y;
+ int minDist = 10000000;
+ RMBoxLoc *cur = _theBoxes->getBoxes(_curLocation);
+
+ for (short cc = 0; cc < cur->_boxes[sourcebox]._numHotspot; cc++)
+ if ((cur->_boxes[sourcebox]._hotspot[cc]._destination) == destbox) {
+ x = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hotx - _pos._x);
+ y = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hoty - _pos._y);
+
+ if ((x * x + y * y) < minDist) {
+ minDist = x * x + y * y;
+ hotspot._x = cur->_boxes[sourcebox]._hotspot[cc]._hotx;
+ hotspot._y = cur->_boxes[sourcebox]._hotspot[cc]._hoty;
+ }
+ }
+
+ return hotspot;
+}
+
+void RMCharacter::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bDrawNow) {
+ prim->getDst() += _fixedScroll;
+
+ CORO_INVOKE_2(RMItem::draw, bigBuf, prim);
+ }
+
+ CORO_END_CODE;
+}
+
+void RMCharacter::newBoxEntered(int nBox) {
+ RMBoxLoc *cur;
+
+ // Recall on ExitBox
+ mpalQueryDoAction(3, _curLocation, _curBox);
+
+ cur = _theBoxes->getBoxes(_curLocation);
+ bool bOldReverse = cur->_boxes[_curBox]._bReversed;
+ _curBox = nBox;
+
+ // If Z is changed, we must remove it from the OT
+ if (cur->_boxes[_curBox]._destZ != _z) {
+ _bRemoveFromOT = true;
+ _z = cur->_boxes[_curBox]._destZ;
+ }
+
+ // Movement management is reversed, only if we are not in the shortest path. If we are in the shortest
+ // path, directly do the DoFrame
+ if (_bMovingWithoutMinpath) {
+ if ((cur->_boxes[_curBox]._bReversed && !bOldReverse) || (!cur->_boxes[_curBox]._bReversed && bOldReverse)) {
+ switch (getCurPattern()) {
+ case PAT_WALKUP:
+ setPattern(PAT_WALKDOWN);
+ break;
+ case PAT_WALKDOWN:
+ setPattern(PAT_WALKUP);
+ break;
+ case PAT_WALKRIGHT:
+ setPattern(PAT_WALKLEFT);
+ break;
+ case PAT_WALKLEFT:
+ setPattern(PAT_WALKRIGHT);
+ break;
+ }
+ }
+ }
+
+ // Recall On EnterBox
+ mpalQueryDoAction(2, _curLocation, _curBox);
+}
+
+void RMCharacter::doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int loc) {
+ CORO_BEGIN_CONTEXT;
+ bool bEndNow;
+ RMBoxLoc *cur;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bEndNow = false;
+ _bEndOfPath = false;
+ _bDrawNow = (_curLocation == loc);
+
+ g_system->lockMutex(_csMove);
+
+ // If we're walking..
+ if (_status != STAND) {
+ // If we are going horizontally
+ if (_walkStatus == 1) {
+ _dx = _walkSpeed * _walkCount;
+ _dy = (int)(_slope * _dx);
+ _pos._x = _lineStart._x + _dx;
+ _pos._y = _lineStart._y + _dy;
+
+ // Right
+ if (((_walkSpeed > 0) && (_pos._x > _lineEnd._x)) || ((_walkSpeed < 0) && (_pos._x < _lineEnd._x))) {
+ _pos = _lineEnd;
+ _status = STAND;
+ _ctx->bEndNow = true;
+ }
+ }
+
+ // If we are going vertical
+ if (_walkStatus == 0) {
+ _dy = _walkSpeed * _walkCount;
+ _dx = (int)(_slope * _dy);
+ _pos._x = _lineStart._x + _dx;
+ _pos._y = _lineStart._y + _dy;
+
+ // Down
+ if (((_walkSpeed > 0) && (_pos._y > _lineEnd._y)) || ((_walkSpeed < 0) && (_pos._y < _lineEnd._y))) {
+ _pos = _lineEnd;
+ _status = STAND;
+ _ctx->bEndNow = true;
+ }
+ }
+
+ // Check if the character came out of the BOX in error, in which case he returns immediately
+ if (inWhichBox(_pos) == -1) {
+ _pos._x = _lineStart._x + _olddx;
+ _pos._y = _lineStart._y + _olddy;
+ }
+
+ // If we have just moved to a temporary location, and is over the shortest path, we stop permanently
+ if (_ctx->bEndNow && _minPath == 0) {
+ if (!_bEndOfPath)
+ CORO_INVOKE_0(stop);
+ _bEndOfPath = true;
+ CoroScheduler.pulseEvent(_hEndOfPath);
+ }
+
+ _walkCount++;
+
+ // Update the character Z. @@@ Should remove only if the Z was changed
+
+ // Check if the box was changed
+ if (!_theBoxes->isInBox(_curLocation, _curBox, _pos))
+ newBoxEntered(inWhichBox(_pos));
+
+ // Update the old coordinates
+ _olddx = _dx;
+ _olddy = _dy;
+ }
+
+ // If we stop
+ if (_status == STAND) {
+ // Check if there is still the shortest path to calculate
+ if (_minPath == 1) {
+ _ctx->cur = _theBoxes->getBoxes(_curLocation);
+
+ // If we still have to go through a box
+ if (_pathCount < _pathLength) {
+ // Check if the box we're going into is active
+ if (_ctx->cur->_boxes[_path[_pathCount - 1]]._bActive) {
+ // Move in a straight line towards the nearest hotspot, taking into account the reversing
+ // NEWBOX = path[pathcount-1]
+ CORO_INVOKE_2(goTo, nearestHotSpot(_path[_pathCount - 1], _path[_pathCount]), _ctx->cur->_boxes[_path[_pathCount - 1]]._bReversed);
+ _pathCount++;
+ } else {
+ // If the box is off, we can only block all
+ // @@@ Whilst this should not happen, because have improved
+ // the search for the minimum path
+ _minPath = 0;
+ if (!_bEndOfPath)
+ CORO_INVOKE_0(stop);
+ _bEndOfPath = true;
+ CoroScheduler.pulseEvent(_hEndOfPath);
+ }
+ } else {
+ // If we have already entered the last box, we just have to move in a straight line towards the
+ // point of arrival
+ // NEWBOX = InWhichBox(pathend)
+ _minPath = 0;
+ CORO_INVOKE_2(goTo, _pathEnd, _ctx->cur->_boxes[inWhichBox(_pathEnd)]._bReversed);
+ }
+ }
+ }
+
+ g_system->unlockMutex(_csMove);
+
+ // Invoke the DoFrame of the item
+ RMItem::doFrame(bigBuf);
+
+ CORO_END_CODE;
+}
+
+bool RMCharacter::endOfPath() {
+ return _bEndOfPath;
+}
+
+void RMCharacter::stop(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _bMoving = false;
+
+ // You never know..
+ _status = STAND;
+ _minPath = 0;
+
+ if (!_bNeedToStop)
+ return;
+
+ _bNeedToStop = false;
+
+ switch (getCurPattern()) {
+ case PAT_WALKUP:
+ setPattern(PAT_STANDUP);
+ break;
+
+ case PAT_WALKDOWN:
+ setPattern(PAT_STANDDOWN);
+ break;
+
+ case PAT_WALKLEFT:
+ setPattern(PAT_STANDLEFT);
+ break;
+
+ case PAT_WALKRIGHT:
+ setPattern(PAT_STANDRIGHT);
+ break;
+
+ default:
+ setPattern(PAT_STANDDOWN);
+ break;
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Check if the character is moving
+ */
+bool RMCharacter::isMoving() {
+ return _bMoving;
+}
+
+inline int RMCharacter::inWhichBox(const RMPoint &pt) {
+ return _theBoxes->whichBox(_curLocation, pt);
+}
+
+
+void RMCharacter::move(CORO_PARAM, RMPoint pt, bool *result) {
+ CORO_BEGIN_CONTEXT;
+ RMPoint dest;
+ int numbox;
+ RMBoxLoc *cur;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _bMoving = true;
+
+ // 0, 0 does not do anything, just stops the character
+ if (pt._x == 0 && pt._y == 0) {
+ _minPath = 0;
+ _status = STAND;
+ CORO_INVOKE_0(stop);
+ if (result)
+ *result = true;
+ return;
+ }
+
+ // If clicked outside the box
+ _ctx->numbox = inWhichBox(pt);
+ if (_ctx->numbox == -1) {
+ // Find neareste point inside the box
+ _ctx->dest = nearestPoint(pt);
+
+ // ???!??
+ if (_ctx->dest == pt)
+ _ctx->dest = invScanLine(pt);
+
+ pt = _ctx->dest;
+ _ctx->numbox = inWhichBox(pt);
+ }
+
+ _ctx->cur = _theBoxes->getBoxes(_curLocation);
+
+ _minPath = 0;
+ _status = STAND;
+ _bMovingWithoutMinpath = true;
+ if (scanLine(pt))
+ CORO_INVOKE_2(goTo, pt, _ctx->cur->_boxes[_ctx->numbox]._bReversed);
+ else if (findPath(inWhichBox(_pos), inWhichBox(pt))) {
+ _bMovingWithoutMinpath = false;
+ _minPath = 1;
+ _pathCount = 1;
+ _pathEnd = pt;
+ } else {
+ // @@@ This case is whether a hotspot is inside a box, but there is
+ // a path to get there. We use the InvScanLine to search around a point
+ _ctx->dest = invScanLine(pt);
+ pt = _ctx->dest;
+
+ if (scanLine(pt))
+ CORO_INVOKE_2(goTo, pt, _ctx->cur->_boxes[_ctx->numbox]._bReversed);
+ else if (findPath(inWhichBox(_pos), inWhichBox(pt))) {
+ _bMovingWithoutMinpath = false;
+ _minPath = 1;
+ _pathCount = 1;
+ _pathEnd = pt;
+ if (result)
+ *result = true;
+ } else {
+ if (result)
+ *result = false;
+ }
+
+ return;
+ }
+
+ if (result)
+ *result = true;
+
+ CORO_END_CODE;
+}
+
+void RMCharacter::setPosition(const RMPoint &pt, int newloc) {
+ RMBoxLoc *box;
+
+ _minPath = 0;
+ _status = STAND;
+ _pos = pt;
+
+ if (newloc != -1)
+ _curLocation = newloc;
+
+ // Update the character's Z value
+ box = _theBoxes->getBoxes(_curLocation);
+ _curBox = inWhichBox(_pos);
+ assert(_curBox != -1);
+ _z = box->_boxes[_curBox]._destZ;
+ _bRemoveFromOT = true;
+}
+
+void RMCharacter::waitForEndMovement(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bMoving)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndOfPath, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+void RMCharacter::setFixedScroll(const RMPoint &fix) {
+ _fixedScroll = fix;
+}
+
+void RMCharacter::setSpeed(int speed) {
+ _curSpeed = speed;
+}
+
+void RMCharacter::removeThis(CORO_PARAM, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bRemoveFromOT)
+ result = true;
+ else
+ CORO_INVOKE_1(RMItem::removeThis, result);
+
+ CORO_END_CODE;
+}
+
+RMCharacter::RMCharacter() {
+ _csMove = g_system->createMutex();
+ _hEndOfPath = CoroScheduler.createEvent(false, false);
+ _minPath = 0;
+ _curSpeed = 3;
+ _bRemoveFromOT = false;
+ _bMoving = false;
+ _curLocation = 0;
+ _curBox = 0;
+ _dx = _dy = 0;
+ _olddx = _olddy = 0;
+ _fx = _fy = _slope = 0;
+ _walkSpeed = _walkStatus = 0;
+ _nextBox = 0;
+ _pathLength = _pathCount = 0;
+ _status = STAND;
+ _theBoxes = NULL;
+ _walkCount = 0;
+ _bEndOfPath = false;
+ _bMovingWithoutMinpath = false;
+ _bDrawNow = false;
+ _bNeedToStop = false;
+
+ memset(_path, 0, sizeof(_path));
+
+ _pos.set(0, 0);
+}
+
+RMCharacter::~RMCharacter() {
+ g_system->deleteMutex(_csMove);
+ CoroScheduler.closeEvent(_hEndOfPath);
+}
+
+void RMCharacter::linkToBoxes(RMGameBoxes *boxes) {
+ _theBoxes = boxes;
+}
+
+/****************************************************************************\
+* RMBox Methods
+\****************************************************************************/
+
+void RMBox::readFromStream(Common::ReadStream &ds) {
+ // Bbox
+ _left = ds.readSint32LE();
+ _top = ds.readSint32LE();
+ _right = ds.readSint32LE();
+ _bottom = ds.readSint32LE();
+
+ // Adjacency
+ for (int i = 0; i < MAXBOXES; i++) {
+ _adj[i] = ds.readSint32LE();
+ }
+
+ // Misc
+ _numHotspot = ds.readSint32LE();
+ _destZ = ds.readByte();
+ byte b = ds.readByte();
+ _bActive = b;
+ b = ds.readByte();
+ _bReversed = b;
+
+ // Reversed expansion space
+ for (int i = 0; i < 30; i++)
+ ds.readByte();
+
+ uint16 w;
+ // Hotspots
+ for (int i = 0; i < _numHotspot; i++) {
+ w = ds.readUint16LE();
+ _hotspot[i]._hotx = w;
+ w = ds.readUint16LE();
+ _hotspot[i]._hoty = w;
+ w = ds.readUint16LE();
+ _hotspot[i]._destination = w;
+ }
+}
+
+/****************************************************************************\
+* RMBoxLoc Methods
+\****************************************************************************/
+
+RMBoxLoc::RMBoxLoc() {
+ _boxes = NULL;
+ _numbBox = 0;
+}
+
+RMBoxLoc::~RMBoxLoc() {
+ delete[] _boxes;
+}
+
+void RMBoxLoc::readFromStream(Common::ReadStream &ds) {
+ char buf[2];
+
+ // ID and version
+ buf[0] = ds.readByte();
+ buf[1] = ds.readByte();
+ byte ver = ds.readByte();
+ assert(buf[0] == 'B' && buf[1] == 'X');
+ assert(ver == 3);
+
+ // Number of boxes
+ _numbBox = ds.readSint32LE();
+
+ // Allocate memory for the boxes
+ _boxes = new RMBox[_numbBox];
+
+ // Read in boxes
+ for (int i = 0; i < _numbBox; i++)
+ _boxes[i].readFromStream(ds);
+}
+
+void RMBoxLoc::recalcAllAdj() {
+ for (int i = 0; i < _numbBox; i++) {
+ Common::fill(_boxes[i]._adj, _boxes[i]._adj + MAXBOXES, 0);
+
+ for (int j = 0; j < _boxes[i]._numHotspot; j++) {
+ if (_boxes[_boxes[i]._hotspot[j]._destination]._bActive)
+ _boxes[i]._adj[_boxes[i]._hotspot[j]._destination] = 1;
+ }
+ }
+}
+
+/****************************************************************************\
+* RMGameBoxes methods
+\****************************************************************************/
+
+RMGameBoxes::RMGameBoxes() {
+ _nLocBoxes = 0;
+ Common::fill(_allBoxes, _allBoxes + GAME_BOXES_SIZE, (RMBoxLoc *)NULL);
+}
+
+RMGameBoxes::~RMGameBoxes() {
+ for (int i = 1; i <= _nLocBoxes; ++i)
+ delete _allBoxes[i];
+}
+
+void RMGameBoxes::init() {
+ // Load boxes from disk
+ _nLocBoxes = 130;
+ for (int i = 1; i <= _nLocBoxes; i++) {
+ RMRes res(10000 + i);
+
+ Common::SeekableReadStream *ds = res.getReadStream();
+
+ _allBoxes[i] = new RMBoxLoc();
+ _allBoxes[i]->readFromStream(*ds);
+
+ _allBoxes[i]->recalcAllAdj();
+
+ delete ds;
+ }
+}
+
+void RMGameBoxes::close() {
+}
+
+RMBoxLoc *RMGameBoxes::getBoxes(int nLoc) {
+ return _allBoxes[nLoc];
+}
+
+int RMGameBoxes::getLocBoxesCount() const {
+ return _nLocBoxes;
+}
+
+bool RMGameBoxes::isInBox(int nLoc, int nBox, const RMPoint &pt) {
+ RMBoxLoc *cur = getBoxes(nLoc);
+
+ if ((pt._x >= cur->_boxes[nBox]._left) && (pt._x <= cur->_boxes[nBox]._right) &&
+ (pt._y >= cur->_boxes[nBox]._top) && (pt._y <= cur->_boxes[nBox]._bottom))
+ return true;
+ else
+ return false;
+}
+
+int RMGameBoxes::whichBox(int nLoc, const RMPoint &punto) {
+ RMBoxLoc *cur = getBoxes(nLoc);
+
+ if (!cur)
+ return -1;
+
+ for (int i = 0; i < cur->_numbBox; i++) {
+ if (cur->_boxes[i]._bActive) {
+ if ((punto._x >= cur->_boxes[i]._left) && (punto._x <= cur->_boxes[i]._right) &&
+ (punto._y >= cur->_boxes[i]._top) && (punto._y <= cur->_boxes[i]._bottom))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void RMGameBoxes::changeBoxStatus(int nLoc, int nBox, int status) {
+ _allBoxes[nLoc]->_boxes[nBox]._bActive = status;
+ _allBoxes[nLoc]->recalcAllAdj();
+}
+
+int RMGameBoxes::getSaveStateSize() {
+ int size = 4;
+
+ for (int i = 1; i <= _nLocBoxes; i++) {
+ size += 4;
+ size += _allBoxes[i]->_numbBox;
+ }
+
+ return size;
+}
+
+void RMGameBoxes::saveState(byte *state) {
+ // Save the number of locations with boxes
+ WRITE_LE_UINT32(state, _nLocBoxes);
+ state += 4;
+
+ // For each location, write out the number of boxes and their status
+ for (int i = 1; i <= _nLocBoxes; i++) {
+ WRITE_LE_UINT32(state, _allBoxes[i]->_numbBox);
+ state += 4;
+
+ for (int j = 0; j < _allBoxes[i]->_numbBox; j++)
+ *state++ = _allBoxes[i]->_boxes[j]._bActive;
+ }
+}
+
+void RMGameBoxes::loadState(byte *state) {
+ // Load number of items
+ int nloc = READ_LE_UINT32(state);
+ state += 4;
+
+ assert(nloc <= _nLocBoxes);
+
+ int nbox;
+ // For each location, read the number of boxes and their status
+ for (int i = 1; i <= nloc; i++) {
+ nbox = READ_LE_UINT32(state);
+ state += 4;
+
+ for (int j = 0; j < nbox ; j++) {
+ if (j < _allBoxes[i]->_numbBox)
+ _allBoxes[i]->_boxes[j]._bActive = *state;
+
+ state++;
+ }
+
+ _allBoxes[i]->recalcAllAdj();
+ }
+}
+
+/****************************************************************************\
+* RMLocation Methods
+\****************************************************************************/
+
+/**
+ * Standard constructor
+ */
+RMLocation::RMLocation() {
+ _nItems = 0;
+ _items = NULL;
+ _buf = NULL;
+ TEMPNumLoc = 0;
+ _cmode = CM_256;
+}
+
+RMPoint RMLocation::TEMPGetTonyStart() {
+ return TEMPTonyStart;
+}
+
+int RMLocation::TEMPGetNumLoc() {
+ return TEMPNumLoc;
+}
+
+/**
+ * Load a location (.LOC) from a given data stream
+ *
+ * @param ds Data stream
+ * @returns True if succeeded OK, false in case of error.
+ */
+bool RMLocation::load(Common::SeekableReadStream &ds) {
+ char id[3];
+
+ // Reset dirty rectangling
+ _prevScroll.set(-1, -1);
+ _prevFixedScroll.set(-1, -1);
+
+ // Check the ID
+ ds.read(id, 3);
+
+ // Check if we are in a LOX
+ if (id[0] == 'L' && id[1] == 'O' && id[2] == 'X')
+ return loadLOX(ds);
+
+ // Otherwise, check that it is a normal LOC
+ if (id[0] != 'L' || id[1] != 'O' || id[2] != 'C')
+ return false;
+
+ // Version
+ byte ver = ds.readByte();
+ assert(ver == 6);
+
+ // Location name
+ _name = readString(ds);
+
+ // Skip the MPAL bailouts (64 bytes)
+ TEMPNumLoc = ds.readSint32LE();
+ TEMPTonyStart._x = ds.readSint32LE();
+ TEMPTonyStart._y = ds.readSint32LE();
+ ds.skip(64 - 4 * 3);
+
+ // Skip flag associated with the background (?)
+ ds.skip(1);
+
+ // Location dimensions
+ int dimx = ds.readSint32LE();
+ int dimy = ds.readSint32LE();
+ _curScroll.set(0, 0);
+
+ // Read the color mode
+ byte cm = ds.readByte();
+ _cmode = (RMColorMode)cm;
+
+ // Initialize the source buffer and read the location
+ switch (_cmode) {
+ case CM_256:
+ _buf = new RMGfxSourceBuffer8;
+ break;
+
+ case CM_65K:
+ _buf = new RMGfxSourceBuffer16;
+ break;
+
+ default:
+ assert(0);
+ break;
+ };
+
+ // Initialize the surface, loading the palette if necessary
+ _buf->init(ds, dimx, dimy, true);
+
+ // Check the size of the location
+// assert(dimy!=512);
+
+ // Number of objects
+ _nItems = ds.readSint32LE();
+
+ // Create and read in the objects
+ if (_nItems > 0)
+ _items = new RMItem[_nItems];
+
+
+ g_vm->freezeTime();
+ for (int i = 0; i < _nItems && !ds.err(); i++)
+ _items[i].readFromStream(ds);
+ g_vm->unfreezeTime();
+
+ return ds.err();
+}
+
+
+bool RMLocation::loadLOX(Common::SeekableReadStream &ds) {
+ // Version
+ byte ver = ds.readByte();
+ assert(ver == 1);
+
+ // Location name
+ _name = readString(ds);
+
+ // Location number
+ TEMPNumLoc = ds.readSint32LE();
+ TEMPTonyStart._x = ds.readSint32LE();
+ TEMPTonyStart._y = ds.readSint32LE();
+
+ // Dimensions
+ int dimx = ds.readSint32LE();
+ int dimy = ds.readSint32LE();
+ _curScroll.set(0, 0);
+
+ // It's always 65K (16-bit) mode
+ _cmode = CM_65K;
+ _buf = new RMGfxSourceBuffer16;
+
+ // Initialize the surface, loading in the palette if necessary
+ _buf->init(ds, dimx, dimy, true);
+
+ // Number of items
+ _nItems = ds.readSint32LE();
+
+ // Create and read objects
+ if (_nItems > 0)
+ _items = new RMItem[_nItems];
+
+ for (int i = 0; i < _nItems && !ds.err(); i++)
+ _items[i].readFromStream(ds, true);
+
+ return ds.err();
+}
+
+
+/**
+ * Draw method overloaded from RMGfxSourceBUffer8
+ */
+void RMLocation::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ bool priorTracking;
+ bool hasChanges;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Set the position of the source scrolling
+ if (_buf->getDimy() > RM_SY || _buf->getDimx() > RM_SX) {
+ prim->setSrc(RMRect(_curScroll, _curScroll + RMPoint(640, 480)));
+ }
+
+ prim->setDst(_fixedScroll);
+
+ // Check whether dirty rects are being tracked, and if there are changes, leave tracking
+ // turned on so a dirty rect will be added for the entire background
+ _ctx->priorTracking = bigBuf.getTrackDirtyRects();
+ _ctx->hasChanges = (_prevScroll != _curScroll) || (_prevFixedScroll != _fixedScroll);
+ bigBuf.setTrackDirtyRects(_ctx->priorTracking && _ctx->hasChanges);
+
+ // Invoke the drawing method fo the image class, which will draw the location background
+ CORO_INVOKE_2(_buf->draw, bigBuf, prim);
+
+ if (_ctx->hasChanges) {
+ _prevScroll = _curScroll;
+ _prevFixedScroll = _fixedScroll;
+ }
+ bigBuf.setTrackDirtyRects(_ctx->priorTracking);
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Prepare a frame, adding the location to the OT list, and all the items that have changed animation frame.
+ */
+void RMLocation::doFrame(RMGfxTargetBuffer *bigBuf) {
+ // If the location is not in the OT list, add it in
+ if (!_nInList)
+ bigBuf->addPrim(new RMGfxPrimitive(this));
+
+ // Process all the location items
+ for (int i = 0; i < _nItems; i++)
+ _items[i].doFrame(bigBuf);
+}
+
+
+RMItem *RMLocation::getItemFromCode(uint32 dwCode) {
+ for (int i = 0; i < _nItems; i++) {
+ if (_items[i].mpalCode() == (int)dwCode)
+ return &_items[i];
+ }
+
+ return NULL;
+}
+
+RMItem *RMLocation::whichItemIsIn(const RMPoint &pt) {
+ int found = -1;
+ int foundSize = 0;
+ int size;
+
+ for (int i = 0; i < _nItems; i++) {
+ size = 0;
+ if (_items[i].isIn(pt, &size)) {
+ if (found == -1 || size < foundSize) {
+ foundSize = size;
+ found = i;
+ }
+ }
+ }
+
+ if (found == -1)
+ return NULL;
+ else
+ return &_items[found];
+}
+
+RMLocation::~RMLocation() {
+ unload();
+}
+
+void RMLocation::unload() {
+ // Clear memory
+ if (_items) {
+ delete[] _items;
+ _items = NULL;
+ }
+
+ // Destroy the buffer
+ if (_buf) {
+ delete _buf;
+ _buf = NULL;
+ }
+}
+
+void RMLocation::updateScrolling(const RMPoint &ptShowThis) {
+ RMPoint oldScroll = _curScroll;
+
+ if (_curScroll._x + 250 > ptShowThis._x) {
+ _curScroll._x = ptShowThis._x - 250;
+ } else if (_curScroll._x + RM_SX - 250 < ptShowThis._x) {
+ _curScroll._x = ptShowThis._x + 250 - RM_SX;
+ } else if (ABS(_curScroll._x + RM_SX / 2 - ptShowThis._x) > 32 && _buf->getDimx() > RM_SX) {
+ if (_curScroll._x + RM_SX / 2 < ptShowThis._x)
+ _curScroll._x++;
+ else
+ _curScroll._x--;
+ }
+
+ if (_curScroll._y + 180 > ptShowThis._y) {
+ _curScroll._y = ptShowThis._y - 180;
+ } else if (_curScroll._y + RM_SY - 180 < ptShowThis._y) {
+ _curScroll._y = ptShowThis._y + 180 - RM_SY;
+ } else if (ABS(_curScroll._y + RM_SY / 2 - ptShowThis._y) > 16 && _buf->getDimy() > RM_SY) {
+ if (_curScroll._y + RM_SY / 2 < ptShowThis._y)
+ _curScroll._y++;
+ else
+ _curScroll._y--;
+ }
+
+ if (_curScroll._x < 0)
+ _curScroll._x = 0;
+ if (_curScroll._y < 0)
+ _curScroll._y = 0;
+ if (_curScroll._x + RM_SX > _buf->getDimx())
+ _curScroll._x = _buf->getDimx() - RM_SX;
+ if (_curScroll._y + RM_SY > _buf->getDimy())
+ _curScroll._y = _buf->getDimy() - RM_SY;
+
+ if (oldScroll != _curScroll) {
+ for (int i = 0; i < _nItems; i++)
+ _items[i].setScrollPosition(_curScroll);
+ }
+}
+
+void RMLocation::setFixedScroll(const RMPoint &scroll) {
+ _fixedScroll = scroll;
+
+ for (int i = 0; i < _nItems; i++)
+ _items[i].setScrollPosition(_curScroll - _fixedScroll);
+}
+
+void RMLocation::setScrollPosition(const RMPoint &scroll) {
+ RMPoint pt = scroll;
+ if (pt._x < 0)
+ pt._x = 0;
+ if (pt._y < 0)
+ pt._y = 0;
+ if (pt._x + RM_SX > _buf->getDimx())
+ pt._x = _buf->getDimx() - RM_SX;
+ if (pt._y + RM_SY > _buf->getDimy())
+ pt._y = _buf->getDimy() - RM_SY;
+
+ _curScroll = pt;
+
+ for (int i = 0; i < _nItems; i++)
+ _items[i].setScrollPosition(_curScroll);
+}
+
+
+void RMLocation::pauseSound(bool bPause) {
+ for (int i = 0; i < _nItems; i++)
+ _items[i].pauseSound(bPause);
+}
+
+/**
+ * Read the current scroll position
+ */
+RMPoint RMLocation::scrollPosition() {
+ return _curScroll;
+}
+
+/****************************************************************************\
+* RMMessage Methods
+\****************************************************************************/
+
+RMMessage::RMMessage(uint32 dwId) {
+ load(dwId);
+}
+
+RMMessage::RMMessage() {
+ _lpMessage = NULL;
+ _nPeriods = 0;
+ for (int i = 0; i < 256; i++)
+ _lpPeriods[i] = 0;
+}
+
+RMMessage::~RMMessage() {
+ if (_lpMessage)
+ globalDestroy(_lpMessage);
+}
+
+void RMMessage::load(uint32 dwId) {
+ _lpMessage = mpalQueryMessage(dwId);
+ assert(_lpMessage != NULL);
+
+ if (_lpMessage)
+ parseMessage();
+}
+
+void RMMessage::parseMessage() {
+ char *p;
+
+ assert(_lpMessage != NULL);
+
+ _nPeriods = 1;
+ p = _lpPeriods[0] = _lpMessage;
+
+ for (;;) {
+ // Find the end of the current period
+ while (*p != '\0')
+ p++;
+
+ // If there is another '0' at the end of the string, the end has been found
+ p++;
+ if (*p == '\0')
+ break;
+
+ // Otherwise there is another line, and remember it's start
+ _lpPeriods[_nPeriods++] = p;
+ }
+}
+
+bool RMMessage::isValid() {
+ return _lpMessage != NULL;
+}
+
+int RMMessage::numPeriods() {
+ return _nPeriods;
+}
+
+char *RMMessage::period(int num) {
+ return _lpPeriods[num];
+}
+
+char *RMMessage::operator[](int num) {
+ return _lpPeriods[num];
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/loc.h b/engines/tony/loc.h
new file mode 100644
index 0000000000..04ba772458
--- /dev/null
+++ b/engines/tony/loc.h
@@ -0,0 +1,582 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_LOC_H
+#define TONY_LOC_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/file.h"
+#include "tony/sound.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* Various defines
+\****************************************************************************/
+
+/**
+ * Valid color modes
+ */
+typedef enum {
+ CM_256,
+ CM_65K
+} RMColorMode;
+
+
+/****************************************************************************\
+* Class declarations
+\****************************************************************************/
+
+/**
+ * Generic palette
+ */
+class RMPalette {
+public:
+ byte _data[1024];
+
+public:
+ void readFromStream(Common::ReadStream &ds);
+};
+
+
+/**
+ * Sound effect of an object
+ */
+class RMSfx {
+public:
+ Common::String _name;
+ FPSfx *_fx;
+ bool _bPlayingLoop;
+
+public:
+ RMSfx();
+ virtual ~RMSfx();
+
+ void play(bool bLoop = false);
+ void setVolume(int vol);
+ void pause(bool bPause);
+ void stop();
+
+ void readFromStream(Common::ReadStream &ds, bool bLOX = false);
+};
+
+
+/**
+ * Object pattern
+ */
+class RMPattern {
+public:
+ // Type of slot
+ enum RMSlotType {
+ DUMMY1 = 0,
+ DUMMY2,
+ SPRITE,
+ SOUND,
+ COMMAND,
+ SPECIAL
+ };
+
+ // Class slot
+ class RMSlot {
+ private:
+ RMPoint _pos; // Child co-ordinates
+
+ public:
+ RMSlotType _type;
+ int _data;
+ byte _flag;
+
+ public:
+ RMPoint pos() {
+ return _pos;
+ }
+
+ void readFromStream(Common::ReadStream &ds, bool bLOX = false);
+ };
+
+public:
+ Common::String _name;
+
+private:
+ int _speed;
+ RMPoint _pos; // Parent coordinates
+ RMPoint _curPos; // Parent + child coordinates
+ int _bLoop;
+ int _nSlots;
+ int _nCurSlot;
+ int _nCurSprite;
+
+ RMSlot *_slots;
+
+ uint32 _nStartTime;
+
+public:
+ RMPattern();
+ virtual ~RMPattern();
+
+ // A warning that the pattern now and the current
+ int init(RMSfx *sfx, bool bPlayP0 = false, byte *bFlag = NULL);
+
+ // Update the pattern, checking to see if it's time to change slot and executing
+ // any associated commands
+ int update(uint32 hEndPattern, byte &bFlag, RMSfx *sfx);
+
+ // Stop a sound effect
+ void stopSfx(RMSfx *sfx);
+
+ // Reads the position of the pattern
+ RMPoint pos();
+
+ void readFromStream(Common::ReadStream &ds, bool bLOX = false);
+
+private:
+ void updateCoord();
+};
+
+
+/**
+ * Sprite (frame) animation of an item
+ */
+class RMSprite : public RMGfxTask {
+public:
+ Common::String _name;
+ RMRect _rcBox;
+
+protected:
+ RMGfxSourceBuffer *_buf;
+
+public:
+ RMSprite();
+ virtual ~RMSprite();
+
+ void init(RMGfxSourceBuffer *buf);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ void setPalette(byte *lpBuf);
+ void getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy);
+ void LOXGetSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy);
+
+ void readFromStream(Common::SeekableReadStream &ds, bool bLOX = false);
+};
+
+
+/**
+ * Data on an item
+ */
+class RMItem : public RMGfxTask {
+public:
+ Common::String _name;
+
+protected:
+ int _z;
+ RMPoint _pos; // Coordinate ancestor
+ RMColorMode _cm;
+ RMPoint _curScroll;
+
+ byte _FX;
+ byte _FXparm;
+
+ virtual int getCurPattern();
+
+private:
+ int _nCurPattern;
+ int _mpalCode;
+ RMPoint _hot;
+ RMRect _rcBox;
+ int _nSprites, _nSfx, _nPatterns;
+ byte _bPal;
+ RMPalette _pal;
+
+ RMSprite *_sprites;
+ RMSfx *_sfx;
+ RMPattern *_patterns;
+
+ byte _bCurFlag;
+ int _nCurSprite;
+ bool _bIsActive;
+ uint32 _hEndPattern;
+ bool _bInitCurPattern;
+
+public:
+ RMPoint calculatePos();
+
+public:
+ RMItem();
+ virtual ~RMItem();
+
+ // Process to make the object move on any animations.
+ // Returns TRUE if it should be redrawn on the next frame
+ bool doFrame(RMGfxTargetBuffer *bigBuf, bool bAddToList = true);
+
+ // Sets the current scrolling position
+ void setScrollPosition(const RMPoint &scroll);
+
+ // Overloading of check whether to remove from active list
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Overloaded Draw
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Overloaded priority: it's based on Z ordering
+ virtual int priority();
+
+ // Pattern number
+ int numPattern();
+
+ // Set anew animation pattern, changing abruptly from the current
+ virtual void setPattern(int nPattern, bool bPlayP0 = false);
+
+ // Set a new status
+ void setStatus(int nStatus);
+
+ bool isIn(const RMPoint &pt, int *size = NULL);
+ RMPoint getHotspot();
+ bool getName(Common::String &name);
+ int mpalCode();
+
+ // Unload
+ void unload();
+
+ // Wait for the end of the current pattern
+ void waitForEndPattern(CORO_PARAM, uint32 hCustomSkip = CORO_INVALID_PID_VALUE);
+
+ // Sets a new hotspot fro the object
+ void changeHotspot(const RMPoint &pt);
+
+ void setInitCurPattern(bool status);
+
+ void playSfx(int nSfx);
+
+ void readFromStream(Common::SeekableReadStream &ds, bool bLOX = false);
+
+ void pauseSound(bool bPause);
+
+protected:
+ // Create a primitive that has as it's task this item
+ virtual RMGfxPrimitive *newItemPrimitive();
+
+ // Allocate memory for the sprites
+ virtual RMGfxSourceBuffer *newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE);
+};
+
+
+#define MAXBOXES 50 // Maximum number of allowed boxes
+#define MAXHOTSPOT 20 // Maximum nimber of allowed hotspots
+
+class RMBox {
+public:
+ struct Hotspot {
+ int _hotx, _hoty; // Hotspot coordinates
+ int _destination; // Hotspot destination
+ };
+
+public:
+ int _left, _top, _right, _bottom; // Vertici bounding boxes
+ int _adj[MAXBOXES]; // List of adjacent bounding boxes
+ int _numHotspot; // Hotspot number
+ uint8 _destZ; // Z value for the bounding box
+ Hotspot _hotspot[MAXHOTSPOT]; // List of hotspots
+
+ bool _bActive;
+ bool _bReversed;
+
+ void readFromStream(Common::ReadStream &ds);
+};
+
+
+class RMBoxLoc {
+public:
+ int _numbBox;
+ RMBox *_boxes;
+
+ void readFromStream(Common::ReadStream &ds);
+
+public:
+ RMBoxLoc();
+ virtual ~RMBoxLoc();
+
+ void recalcAllAdj();
+};
+
+#define GAME_BOXES_SIZE 200
+
+class RMGameBoxes {
+protected:
+ RMBoxLoc *_allBoxes[GAME_BOXES_SIZE];
+ int _nLocBoxes;
+
+public:
+ RMGameBoxes();
+ ~RMGameBoxes();
+
+ void init();
+ void close();
+
+ // Get binding boxes for a given location
+ RMBoxLoc *getBoxes(int nLoc);
+ int getLocBoxesCount() const;
+
+ // Return the box which contains a given point
+ int whichBox(int nLoc, const RMPoint &pt);
+
+ // Check whether a point is inside a given box
+ bool isInBox(int nLoc, int nBox, const RMPoint &pt);
+
+ // Change the status of a box
+ void changeBoxStatus(int nLoc, int nBox, int status);
+
+ // Save state handling
+ int getSaveStateSize();
+ void saveState(byte *buf);
+ void loadState(byte *buf);
+};
+
+class RMCharacter : protected RMItem {
+public:
+ enum Patterns {
+ PAT_STANDUP = 1,
+ PAT_STANDDOWN,
+ PAT_STANDLEFT,
+ PAT_STANDRIGHT,
+ PAT_WALKUP,
+ PAT_WALKDOWN,
+ PAT_WALKLEFT,
+ PAT_WALKRIGHT
+ };
+
+private:
+ enum CharacterStatus {
+ STAND,
+ WALK
+ };
+
+ signed short _walkCount;
+ int _dx, _dy, _olddx, _olddy;
+ float _fx, _fy, _slope;
+ RMPoint _lineStart, _lineEnd, _pathEnd;
+ signed char _walkSpeed, _walkStatus;
+ char _minPath;
+ short _nextBox;
+ short _path[MAXBOXES];
+ short _pathLength, _pathCount;
+ int _curBox;
+
+ CharacterStatus _status;
+ int _curSpeed;
+ bool _bEndOfPath;
+ uint32 _hEndOfPath;
+ OSystem::MutexRef _csMove;
+ int _curLocation;
+ bool _bRemoveFromOT;
+ bool _bMovingWithoutMinpath;
+ RMGameBoxes *_theBoxes;
+
+ RMPoint _fixedScroll;
+
+private:
+ int inWhichBox(const RMPoint &pt);
+
+ short findPath(short source, short destination);
+ RMPoint searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint point);
+ RMPoint nearestPoint(const RMPoint &punto);
+
+ void goTo(CORO_PARAM, RMPoint destcoord, bool bReversed = false);
+ short scanLine(const RMPoint &point);
+ RMPoint invScanLine(const RMPoint &point);
+ RMPoint nearestHotSpot(int sourcebox, int destbox);
+
+ void newBoxEntered(int nBox);
+
+protected:
+ bool _bMoving;
+ bool _bDrawNow;
+ bool _bNeedToStop;
+
+public:
+ RMCharacter();
+ virtual ~RMCharacter();
+
+ void linkToBoxes(RMGameBoxes *theBoxes);
+
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Update the position of a character
+ void doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int loc);
+
+ // Overloaded draw
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // TRUE if you just stopped
+ bool endOfPath();
+
+ // Change the pattern of a character to STOP
+ virtual void stop(CORO_PARAM);
+
+ // Check if the character is moving
+ bool isMoving();
+
+ // Move the character to a certain position
+ void move(CORO_PARAM, RMPoint pt, bool *result = NULL);
+
+ // Place the character in a certain position WITHOUT moving
+ void setPosition(const RMPoint &pt, int newloc = -1);
+
+ // Wait for the end of movement
+ void waitForEndMovement(CORO_PARAM);
+
+ void setFixedScroll(const RMPoint &fix);
+ void setSpeed(int speed);
+};
+
+
+class RMWipe : public RMGfxTask {
+private:
+ bool _bFading;
+ bool _bEndFade;
+ bool _bUnregister;
+ uint32 _hUnregistered;
+ int _nFadeStep;
+ uint32 _hEndOfFade;
+ bool _bMustRegister;
+
+ RMItem _wip0r;
+
+public:
+ RMWipe();
+ virtual ~RMWipe();
+
+ void doFrame(RMGfxTargetBuffer &bigBuf);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ void initFade(int type);
+ void closeFade();
+ void waitForFadeEnd(CORO_PARAM);
+
+ virtual void unregister();
+ virtual void removeThis(CORO_PARAM, bool &result);
+ virtual int priority();
+};
+
+
+/**
+ * Location
+ */
+class RMLocation : public RMGfxTaskSetPrior {
+public:
+ Common::String _name; // Name
+
+private:
+ RMColorMode _cmode; // Color mode
+ RMGfxSourceBuffer *_buf; // Location picture
+
+ int _nItems; // Number of objects
+ RMItem *_items; // Objects
+
+ RMPoint _curScroll; // Current scroll position
+ RMPoint _fixedScroll;
+
+ RMPoint _prevScroll; // Previous scroll position
+ RMPoint _prevFixedScroll;
+
+public:
+ // @@@@@@@@@@@@@@@@@@@@@@@
+
+ RMPoint TEMPTonyStart;
+ RMPoint TEMPGetTonyStart();
+
+ int TEMPNumLoc;
+ int TEMPGetNumLoc();
+
+public:
+ RMLocation();
+ virtual ~RMLocation();
+
+ // Load variations
+ bool load(Common::SeekableReadStream &ds);
+ bool loadLOX(Common::SeekableReadStream &ds);
+
+ // Unload
+ void unload();
+
+ // Overloaded draw
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Prepare a frame by drawing the location and all it's items
+ void doFrame(RMGfxTargetBuffer *bigBuf);
+
+ // Return the item at a given point
+ RMItem *whichItemIsIn(const RMPoint &pt);
+
+ // Return the item based on it's MPAL code
+ RMItem *getItemFromCode(uint32 dwCode);
+
+ // Set the current scroll position
+ void setScrollPosition(const RMPoint &scroll);
+
+ // Sets an additinal offset for scrolling
+ void setFixedScroll(const RMPoint &scroll);
+
+ // Update the scrolling coordinates to display the specified point
+ void updateScrolling(const RMPoint &ptShowThis);
+
+ // Read the current scroll position
+ RMPoint scrollPosition();
+
+ // Pause sound
+ void pauseSound(bool bPause);
+};
+
+
+/**
+ * MPAL message, composed of more ASCIIZ
+ */
+class RMMessage {
+private:
+ char *_lpMessage;
+ char *_lpPeriods[256];
+ int _nPeriods;
+
+private:
+ void parseMessage();
+
+public:
+ RMMessage();
+ RMMessage(uint32 dwId);
+ virtual ~RMMessage();
+
+ void load(uint32 dwId);
+ bool isValid();
+ int numPeriods();
+ char *period(int num);
+ char *operator[](int num);
+};
+
+} // End of namespace Tony
+
+#endif /* TONY_H */
diff --git a/engines/tony/module.mk b/engines/tony/module.mk
new file mode 100644
index 0000000000..d66cf6f065
--- /dev/null
+++ b/engines/tony/module.mk
@@ -0,0 +1,33 @@
+MODULE := engines/tony
+
+MODULE_OBJS := \
+ custom.o \
+ debugger.o \
+ detection.o \
+ font.o \
+ game.o \
+ gfxcore.o \
+ gfxengine.o \
+ globals.o \
+ input.o \
+ inventory.o \
+ loc.o \
+ sound.o \
+ tony.o \
+ tonychar.o \
+ utils.o \
+ window.o \
+ mpal/expr.o \
+ mpal/loadmpc.o \
+ mpal/memory.o \
+ mpal/mpal.o \
+ mpal/mpalutils.o \
+ mpal/lzo.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_TONY), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/tony/mpal/expr.cpp b/engines/tony/mpal/expr.cpp
new file mode 100644
index 0000000000..824cd91651
--- /dev/null
+++ b/engines/tony/mpal/expr.cpp
@@ -0,0 +1,365 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/memory.h"
+#include "tony/mpal/mpaldll.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+
+/**
+ * Duplicate a mathematical expression.
+ *
+ * @param h Handle to the original expression
+ * @retruns Pointer to the cloned expression
+ */
+static byte *duplicateExpression(MpalHandle h) {
+ byte *orig, *clone;
+
+ orig = (byte *)globalLock(h);
+
+ int num = *(byte *)orig;
+ LpExpression one = (LpExpression)(orig+1);
+
+ clone = (byte *)globalAlloc(GMEM_FIXED, sizeof(Expression) * num + 1);
+ LpExpression two = (LpExpression)(clone + 1);
+
+ memcpy(clone, orig, sizeof(Expression) * num + 1);
+
+ for (int i = 0; i < num; i++) {
+ if (one->_type == ELT_PARENTH) {
+ two->_type = ELT_PARENTH2;
+ two->_val._pson = duplicateExpression(two->_val._son);
+ }
+
+ ++one;
+ ++two;
+ }
+
+ globalUnlock(h);
+ return clone;
+}
+
+static int Compute(int a, int b, byte symbol) {
+ switch (symbol) {
+ case OP_MUL:
+ return a * b;
+ case OP_DIV:
+ return a / b;
+ case OP_MODULE:
+ return a % b;
+ case OP_ADD:
+ return a + b;
+ case OP_SUB:
+ return a - b;
+ case OP_SHL:
+ return a << b;
+ case OP_SHR:
+ return a >> b;
+ case OP_MINOR:
+ return a < b;
+ case OP_MAJOR:
+ return a > b;
+ case OP_MINEQ:
+ return a <= b;
+ case OP_MAJEQ:
+ return a >= b;
+ case OP_EQUAL:
+ return a == b;
+ case OP_NOEQUAL:
+ return a != b;
+ case OP_BITAND:
+ return a & b;
+ case OP_BITXOR:
+ return a ^ b;
+ case OP_BITOR:
+ return a | b;
+ case OP_AND:
+ return a && b;
+ case OP_OR:
+ return a || b;
+ default:
+ GLOBALS._mpalError = 1;
+ break;
+ }
+
+ return 0;
+}
+
+static void solve(LpExpression one, int num) {
+ LpExpression two, three;
+
+ while (num > 1) {
+ two = one + 1;
+ if ((two->_symbol == 0) || (one->_symbol & 0xF0) <= (two->_symbol & 0xF0)) {
+ two->_val._num = Compute(one->_val._num, two->_val._num, one->_symbol);
+ memmove(one, two, (num - 1) * sizeof(Expression));
+ --num;
+ } else {
+ int j = 1;
+ three = two + 1;
+ while ((three->_symbol != 0) && (two->_symbol & 0xF0) > (three->_symbol & 0xF0)) {
+ ++two;
+ ++three;
+ ++j;
+ }
+
+ three->_val._num = Compute(two->_val._num, three->_val._num, two->_symbol);
+ memmove(two, three, (num - j - 1) * sizeof(Expression));
+ --num;
+ }
+ }
+}
+
+
+/**
+ * Calculates the result of a mathematical expression, replacing the current
+ * value of any variable.
+ *
+ * @param expr Pointer to an expression duplicated by DuplicateExpression
+ * @returns Value
+ */
+static int evaluateAndFreeExpression(byte *expr) {
+ int num = *expr;
+ LpExpression one = (LpExpression)(expr + 1);
+
+ // 1) Substitutions of variables
+ LpExpression cur = one;
+ for (int i = 0; i < num; i++, cur++) {
+ if (cur->_type == ELT_VAR) {
+ cur->_type = ELT_NUMBER;
+ cur->_val._num = varGetValue(cur->_val._name);
+ }
+ }
+
+ // 2) Replacement of brackets (using recursive calls)
+ cur = one;
+ for (int i = 0; i < num; i++, cur++) {
+ if (cur->_type == ELT_PARENTH2) {
+ cur->_type = ELT_NUMBER;
+ cur->_val._num = evaluateAndFreeExpression(cur->_val._pson);
+ }
+ }
+
+ // 3) algebraic resolution
+ solve(one, num);
+ int val = one->_val._num;
+ globalDestroy(expr);
+
+ return val;
+}
+
+
+/**
+ * Parses a mathematical expression from the MPC file
+ *
+ * @param buf Buffer containing the expression to evaluate
+ * @param h Pointer to a handle that, at the end of execution,
+ * will point to the area of memory containing the parsed expression
+ * @returns Pointer to the buffer immediately after the expression, or NULL if error.
+ */
+const byte *parseExpression(const byte *lpBuf, MpalHandle *h) {
+ byte *start;
+
+ uint32 num = *lpBuf;
+ lpBuf++;
+
+ if (num == 0)
+ return NULL;
+
+ *h = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, num * sizeof(Expression) + 1);
+ if (*h == NULL)
+ return NULL;
+
+ start = (byte *)globalLock(*h);
+ *start = (byte)num;
+
+ LpExpression cur = (LpExpression)(start + 1);
+
+ for (uint32 i = 0;i < num; i++) {
+ cur->_type = *(lpBuf);
+
+ // *(lpBuf + 1) contains the unary operator, unused => skipped
+ lpBuf += 2;
+
+ switch (cur->_type) {
+ case ELT_NUMBER:
+ cur->_val._num = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ case ELT_VAR:
+ cur->_val._name = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, (*lpBuf) + 1);
+ if (cur->_val._name == NULL)
+ return NULL;
+ memcpy(cur->_val._name, lpBuf + 1, *lpBuf);
+ lpBuf += *lpBuf + 1;
+ break;
+
+ case ELT_PARENTH:
+ lpBuf = parseExpression(lpBuf, &cur->_val._son);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ cur->_symbol = *lpBuf;
+ lpBuf++;
+
+ cur++;
+ }
+
+ if (*lpBuf != 0)
+ return NULL;
+
+ lpBuf++;
+
+ return lpBuf;
+}
+
+
+/**
+ * Calculate the value of a mathamatical expression
+ *
+ * @param h Handle to the expression
+ * @returns Numeric value
+ */
+int evaluateExpression(MpalHandle h) {
+ lockVar();
+ int ret = evaluateAndFreeExpression(duplicateExpression(h));
+ unlockVar();
+
+ return ret;
+}
+
+/**
+ * Compare two mathematical expressions together
+ *
+ * @param h1 Expression to be compared
+ * @param h2 Expression to be compared
+ */
+bool compareExpressions(MpalHandle h1, MpalHandle h2) {
+ byte *e1, *e2;
+
+ e1 = (byte *)globalLock(h1);
+ e2 = (byte *)globalLock(h2);
+
+ int num1 = *(byte *)e1;
+ int num2 = *(byte *)e2;
+
+ if (num1 != num2) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+
+ LpExpression one = (LpExpression)(e1 + 1);
+ LpExpression two = (LpExpression)(e2 + 1);
+
+ for (int i = 0; i < num1; i++) {
+ if (one->_type != two->_type || (i != num1 - 1 && one->_symbol != two->_symbol)) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+
+ switch (one->_type) {
+ case ELT_NUMBER:
+ if (one->_val._num != two->_val._num) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+ break;
+
+ case ELT_VAR:
+ if (strcmp(one->_val._name, two->_val._name) != 0) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+ break;
+
+ case ELT_PARENTH:
+ if (!compareExpressions(one->_val._son, two->_val._son)) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+ break;
+ }
+
+ ++one;
+ ++two;
+ }
+
+ globalUnlock(h1);
+ globalUnlock(h2);
+
+ return true;
+}
+
+/**
+ * Frees an expression that was previously parsed
+ *
+ * @param h Handle for the expression
+ */
+void freeExpression(MpalHandle h) {
+ byte *data = (byte *)globalLock(h);
+ int num = *data;
+ LpExpression cur = (LpExpression)(data + 1);
+
+ for (int i = 0; i < num; ++i, ++cur) {
+ switch (cur->_type) {
+ case ELT_VAR:
+ globalDestroy(cur->_val._name);
+ break;
+
+ case ELT_PARENTH:
+ freeExpression(cur->_val._son);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ globalUnlock(h);
+ globalFree(h);
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/expr.h b/engines/tony/mpal/expr.h
new file mode 100644
index 0000000000..405624b4fe
--- /dev/null
+++ b/engines/tony/mpal/expr.h
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef MPAL_EXPR_H
+#define MPAL_EXPR_H
+
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/**
+ * @defgroup Mathamatical operations
+ */
+//@{
+
+#define OP_MUL ((1 << 4) | 0)
+#define OP_DIV ((1 << 4) | 1)
+#define OP_MODULE ((1 << 4) | 2)
+#define OP_ADD ((2 << 4) | 0)
+#define OP_SUB ((2 << 4) | 1)
+#define OP_SHL ((3 << 4) | 0)
+#define OP_SHR ((3 << 4) | 1)
+#define OP_MINOR ((4 << 4) | 0)
+#define OP_MAJOR ((4 << 4) | 1)
+#define OP_MINEQ ((4 << 4) | 2)
+#define OP_MAJEQ ((4 << 4) | 3)
+#define OP_EQUAL ((5 << 4) | 0)
+#define OP_NOEQUAL ((5 << 4) | 1)
+#define OP_BITAND ((6 << 4) | 0)
+#define OP_BITXOR ((7 << 4) | 0)
+#define OP_BITOR ((8 << 4) | 0)
+#define OP_AND ((9 << 4) | 0)
+#define OP_OR ((10 << 4) | 0)
+
+//@}
+
+/**
+ * @defgroup Structures
+ */
+
+//@{
+/**
+ * Mathamatical framework to manage operations
+ */
+typedef struct {
+ byte _type; // Object Type (see enum ExprListTypes)
+
+ union {
+ int _num; // Identifier (if type == ELT_NUMBER)
+ char *_name; // Variable name (if type == ELT_VAR)
+ MpalHandle _son; // Handle expressions (if type == ELT_PARENTH)
+ byte *_pson; // Handle lockato (if type == ELT_PARENTH2)
+ } _val;
+
+ byte _symbol; // Mathematic symbols (see #define OP_*)
+
+} Expression;
+typedef Expression *LpExpression;
+
+//@}
+
+/**
+ * Object types that can be contained in an EXPRESSION structure
+ */
+enum ExprListTypes {
+ ELT_NUMBER = 1,
+ ELT_VAR = 2,
+ ELT_PARENTH = 3,
+ ELT_PARENTH2 = 4
+};
+
+/****************************************************************************\
+* Function Prototypes
+\****************************************************************************/
+
+/**
+ * Parses a mathematical expression from the MPC file
+ *
+ * @param buf Buffer containing the expression to evaluate
+ * @param h Pointer to a handle that, at the end of execution,
+ * will point to the area of memory containing the parsed expression
+ * @returns Pointer to the buffer immediately after the expression, or NULL if error.
+ */
+const byte *parseExpression(const byte *lpBuf, MpalHandle *h);
+
+/**
+ * Calculate the value of a mathamatical expression
+ *
+ * @param h Handle to the expression
+ * @returns Numeric value
+ */
+int evaluateExpression(MpalHandle h);
+
+/**
+ * Compare two mathematical expressions together
+ *
+ * @param h1 Expression to be compared
+ * @param h2 Expression to be compared
+ */
+bool compareExpressions(MpalHandle h1, MpalHandle h2);
+
+/**
+ * Frees an expression that was previously parsed
+ *
+ * @param h Handle for the expression
+ */
+void freeExpression(MpalHandle h);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
diff --git a/engines/tony/mpal/loadmpc.cpp b/engines/tony/mpal/loadmpc.cpp
new file mode 100644
index 0000000000..9c45cdf982
--- /dev/null
+++ b/engines/tony/mpal/loadmpc.cpp
@@ -0,0 +1,788 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "mpal.h"
+#include "mpaldll.h"
+#include "memory.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Static functions
+\****************************************************************************/
+
+static bool compareCommands(struct Command *cmd1, struct Command *cmd2) {
+ if (cmd1->_type == 2 && cmd2->_type == 2) {
+ if (strcmp(cmd1->_lpszVarName, cmd2->_lpszVarName) == 0 &&
+ compareExpressions(cmd1->_expr, cmd2->_expr))
+ return true;
+ else
+ return false;
+ } else
+ return (memcmp(cmd1, cmd2, sizeof(struct Command)) == 0);
+}
+
+/**
+ * Parses a script from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled script.
+ * @param lpmsScript Pointer to a structure that will be filled with the
+ * data of the script.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ */
+static const byte *ParseScript(const byte *lpBuf, LpMpalScript lpmsScript) {
+ lpmsScript->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ lpmsScript->_nMoments = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ int curCmd = 0;
+
+ for (uint i = 0; i < lpmsScript->_nMoments; i++) {
+ lpmsScript->_moment[i]._dwTime = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_moment[i]._nCmds = *lpBuf;
+ lpBuf++;
+
+ for (int j = 0; j < lpmsScript->_moment[i]._nCmds; j++) {
+ lpmsScript->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+ switch (lpmsScript->_command[curCmd]._type) {
+ case 1:
+ lpmsScript->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmsScript->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ case 2: { // Variable assign
+ int len = *lpBuf;
+ lpBuf++;
+ lpmsScript->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmsScript->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+ memcpy(lpmsScript->_command[curCmd]._lpszVarName, lpBuf, len);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmsScript->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+ }
+ default:
+ return NULL;
+ }
+
+ lpmsScript->_moment[i]._cmdNum[j] = curCmd;
+ curCmd++;
+ }
+ }
+ return lpBuf;
+}
+
+/**
+ * Frees a script allocated via a previous call to ParseScript
+ *
+ * @param lpmsScript Pointer to a script structure
+ */
+static void FreeScript(LpMpalScript lpmsScript) {
+ for (int i = 0; i < MAX_COMMANDS_PER_SCRIPT && (lpmsScript->_command[i]._type); ++i, ++lpmsScript) {
+ if (lpmsScript->_command[i]._type == 2) {
+ // Variable Assign
+ globalDestroy(lpmsScript->_command[i]._lpszVarName);
+ freeExpression(lpmsScript->_command[i]._expr);
+ }
+ }
+}
+
+/**
+ * Parses a dialog from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled dialog.
+ * @param lpmdDialog Pointer to a structure that will be filled with the
+ * data of the dialog.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ */
+static const byte *parseDialog(const byte *lpBuf, LpMpalDialog lpmdDialog) {
+ byte *lpLock;
+
+ lpmdDialog->_nObj = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ // Periods
+ uint32 num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (num >= MAX_PERIODS_PER_DIALOG - 1)
+ error("Too much periods in dialog #%d", lpmdDialog->_nObj);
+
+ uint32 i;
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_periodNums[i] = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_periods[i] = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, *lpBuf + 1);
+ lpLock = (byte *)globalLock(lpmdDialog->_periods[i]);
+ Common::copy(lpBuf + 1, lpBuf + 1 + *lpBuf, lpLock);
+ globalUnlock(lpmdDialog->_periods[i]);
+ lpBuf += (*lpBuf) + 1;
+ }
+
+ lpmdDialog->_periodNums[i] = 0;
+ lpmdDialog->_periods[i] = NULL;
+
+ // Groups
+ num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ uint32 curCmd = 0;
+
+ if (num >= MAX_GROUPS_PER_DIALOG)
+ error("Too much groups in dialog #%d", lpmdDialog->_nObj);
+
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_group[i]._num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_group[i]._nCmds = *lpBuf; lpBuf++;
+
+ if (lpmdDialog->_group[i]._nCmds >= MAX_COMMANDS_PER_GROUP)
+ error("Too much commands in group #%d in dialog #%d", lpmdDialog->_group[i]._num, lpmdDialog->_nObj);
+
+ for (uint32 j = 0; j < lpmdDialog->_group[i]._nCmds; j++) {
+ lpmdDialog->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+
+ switch (lpmdDialog->_command[curCmd]._type) {
+ // Call custom function
+ case 1:
+ lpmdDialog->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_command[curCmd]._arg1 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg2 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg3 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg4 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ // Variable assign
+ case 2: {
+ uint32 len = *lpBuf;
+ lpBuf++;
+ lpmdDialog->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmdDialog->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+
+ Common::copy(lpBuf, lpBuf + len, lpmdDialog->_command[curCmd]._lpszVarName);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmdDialog->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+ }
+ // Do Choice
+ case 3:
+ lpmdDialog->_command[curCmd]._nChoice = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ uint32 kk;
+ for (kk = 0;kk < curCmd; kk++) {
+ if (compareCommands(&lpmdDialog->_command[kk], &lpmdDialog->_command[curCmd])) {
+ lpmdDialog->_group[i]._cmdNum[j] = kk;
+
+ // Free any data allocated for the duplictaed command
+ if (lpmdDialog->_command[curCmd]._type == 2) {
+ globalDestroy(lpmdDialog->_command[curCmd]._lpszVarName);
+ freeExpression(lpmdDialog->_command[curCmd]._expr);
+
+ lpmdDialog->_command[curCmd]._lpszVarName = NULL;
+ lpmdDialog->_command[curCmd]._expr = 0;
+ lpmdDialog->_command[curCmd]._type = 0;
+ }
+ break;
+ }
+ }
+
+ if (kk == curCmd) {
+ lpmdDialog->_group[i]._cmdNum[j] = curCmd;
+ curCmd++;
+ }
+ }
+ }
+
+ if (curCmd >= MAX_COMMANDS_PER_DIALOG)
+ error("Too much commands in dialog #%d", lpmdDialog->_nObj);
+
+ // Choices
+ num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (num >= MAX_CHOICES_PER_DIALOG)
+ error("Too much choices in dialog #%d", lpmdDialog->_nObj);
+
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_choice[i]._nChoice = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ uint32 num2 = *lpBuf++;
+
+ if (num2 >= MAX_SELECTS_PER_CHOICE)
+ error("Too much selects in choice #%d in dialog #%d", lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj);
+
+ for (uint32 j = 0; j < num2; j++) {
+ // When
+ switch (*lpBuf++) {
+ case 0:
+ lpmdDialog->_choice[i]._select[j]._when = NULL;
+ break;
+
+ case 1:
+ lpBuf = parseExpression(lpBuf, &lpmdDialog->_choice[i]._select[j]._when);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+
+ case 2:
+ return NULL;
+ }
+
+ // Attrib
+ lpmdDialog->_choice[i]._select[j]._attr = *lpBuf++;
+
+ // Data
+ lpmdDialog->_choice[i]._select[j]._dwData = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ // PlayGroup
+ uint32 num3 = *lpBuf++;
+
+ if (num3 >= MAX_PLAYGROUPS_PER_SELECT)
+ error("Too much playgroups in select #%d in choice #%d in dialog #%d", j, lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj);
+
+ for (uint32 z = 0; z < num3; z++) {
+ lpmdDialog->_choice[i]._select[j]._wPlayGroup[z] = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ }
+
+ lpmdDialog->_choice[i]._select[j]._wPlayGroup[num3] = 0;
+ }
+
+ // Mark the last selection
+ lpmdDialog->_choice[i]._select[num2]._dwData = 0;
+ }
+
+ lpmdDialog->_choice[num]._nChoice = 0;
+
+ return lpBuf;
+}
+
+
+/**
+ * Parses an item from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled dialog.
+ * @param lpmiItem Pointer to a structure that will be filled with the
+ * data of the item.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ * @remarks It's necessary that the structure that is passed has been
+ * completely initialized to 0 beforehand.
+ */
+static const byte *parseItem(const byte *lpBuf, LpMpalItem lpmiItem) {
+ lpmiItem->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ byte len = *lpBuf;
+ lpBuf++;
+ memcpy(lpmiItem->_lpszDescribe, lpBuf, MIN((byte)127, len));
+ lpBuf += len;
+
+ if (len >= MAX_DESCRIBE_SIZE)
+ error("Describe too long in item #%d", lpmiItem->_nObj);
+
+ lpmiItem->_nActions=*lpBuf;
+ lpBuf++;
+
+ // Allocation action
+ if (lpmiItem->_nActions > 0)
+ lpmiItem->_action = (ItemAction *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(struct ItemAction) * (int)lpmiItem->_nActions);
+
+ uint32 curCmd = 0;
+
+ for (uint32 i = 0; i < lpmiItem->_nActions; i++) {
+ lpmiItem->_action[i]._num = *lpBuf;
+ lpBuf++;
+
+ lpmiItem->_action[i]._wParm = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (lpmiItem->_action[i]._num == 0xFF) {
+ lpmiItem->_action[i]._wTime = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ lpmiItem->_action[i]._perc = *lpBuf;
+ lpBuf++;
+ }
+
+ if (*lpBuf == 0) {
+ lpBuf++;
+ lpmiItem->_action[i]._when = NULL;
+ } else {
+ lpBuf++;
+ lpBuf = parseExpression(lpBuf,&lpmiItem->_action[i]._when);
+ if (lpBuf == NULL)
+ return NULL;
+ }
+
+ lpmiItem->_action[i]._nCmds=*lpBuf;
+ lpBuf++;
+
+ if (lpmiItem->_action[i]._nCmds >= MAX_COMMANDS_PER_ACTION)
+ error("Too much commands in action #%d in item #%d", lpmiItem->_action[i]._num, lpmiItem->_nObj);
+
+ for (uint32 j = 0; j < lpmiItem->_action[i]._nCmds; j++) {
+ lpmiItem->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+ switch (lpmiItem->_command[curCmd]._type) {
+ case 1: // Call custom function
+ lpmiItem->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmiItem->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ case 2: // Variable assign
+ len = *lpBuf;
+ lpBuf++;
+ lpmiItem->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmiItem->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+ memcpy(lpmiItem->_command[curCmd]._lpszVarName, lpBuf, len);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmiItem->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ uint32 kk;
+ for (kk = 0; kk < curCmd; kk++) {
+ if (compareCommands(&lpmiItem->_command[kk], &lpmiItem->_command[curCmd])) {
+ lpmiItem->_action[i]._cmdNum[j] = kk;
+
+ // Free any data allocated for the duplictaed command
+ if (lpmiItem->_command[curCmd]._type == 2) {
+ globalDestroy(lpmiItem->_command[curCmd]._lpszVarName);
+ freeExpression(lpmiItem->_command[curCmd]._expr);
+
+ lpmiItem->_command[curCmd]._lpszVarName = NULL;
+ lpmiItem->_command[curCmd]._expr = 0;
+ lpmiItem->_command[curCmd]._type = 0;
+ }
+ break;
+ }
+ }
+
+ if (kk == curCmd) {
+ lpmiItem->_action[i]._cmdNum[j] = curCmd;
+ curCmd++;
+
+ if (curCmd >= MAX_COMMANDS_PER_ITEM) {
+ error("Too much commands in item #%d", lpmiItem->_nObj);
+ //curCmd=0;
+ }
+ }
+ }
+ }
+
+ lpmiItem->_dwRes = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ return lpBuf;
+}
+
+/**
+ * Frees an item parsed from a prior call to ParseItem
+ *
+ * @param lpmiItem Pointer to an item structure
+ */
+static void freeItem(LpMpalItem lpmiItem) {
+ // Free the actions
+ if (lpmiItem->_action) {
+ for (int i = 0; i < lpmiItem->_nActions; ++i) {
+ if (lpmiItem->_action[i]._when != 0)
+ freeExpression(lpmiItem->_action[i]._when);
+ }
+
+ globalDestroy(lpmiItem->_action);
+ }
+
+ // Free the commands
+ for (int i = 0; i < MAX_COMMANDS_PER_ITEM && (lpmiItem->_command[i]._type); ++i) {
+ if (lpmiItem->_command[i]._type == 2) {
+ // Variable Assign
+ globalDestroy(lpmiItem->_command[i]._lpszVarName);
+ freeExpression(lpmiItem->_command[i]._expr);
+ }
+ }
+}
+
+/**
+ * Parses a location from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled location.
+ * @param lpmiLocation Pointer to a structure that will be filled with the
+ * data of the location.
+ * @returns Pointer to the buffer after the location, or NULL on failure.
+ */
+static const byte *ParseLocation(const byte *lpBuf, LpMpalLocation lpmlLocation) {
+ lpmlLocation->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmlLocation->_dwXlen = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmlLocation->_dwYlen = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmlLocation->_dwPicRes = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ return lpBuf;
+}
+
+
+/****************************************************************************\
+* Exported functions
+\****************************************************************************/
+/**
+ * @defgroup Exported functions
+ */
+//@{
+
+/**
+ * Reads and interprets the MPC file, and create structures for various directives
+ * in the global variables
+ *
+ * @param lpBuf Buffer containing the MPC file data, excluding the header.
+ * @returns True if succeeded OK, false if failure.
+ */
+bool parseMpc(const byte *lpBuf) {
+ byte *lpTemp;
+
+ // 1. Variables
+ if (lpBuf[0] != 'V' || lpBuf[1] != 'A' || lpBuf[2] != 'R' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nVars = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ GLOBALS._hVars = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalVar) * (uint32)GLOBALS._nVars);
+ if (GLOBALS._hVars == NULL)
+ return false;
+
+ GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars);
+
+ for (uint16 i = 0; i < GLOBALS._nVars; i++) {
+ uint16 wLen = *(const byte *)lpBuf;
+ lpBuf++;
+ memcpy(GLOBALS._lpmvVars->_lpszVarName, lpBuf, MIN(wLen, (uint16)32));
+ lpBuf += wLen;
+ GLOBALS._lpmvVars->_dwVal = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ lpBuf++; // Skip 'ext'
+ GLOBALS._lpmvVars++;
+ }
+
+ globalUnlock(GLOBALS._hVars);
+
+ // 2. Messages
+ if (lpBuf[0] != 'M' || lpBuf[1] != 'S' || lpBuf[2] != 'G' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nMsgs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+#ifdef NEED_LOCK_MSGS
+ GLOBALS._hMsgs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalMsg) * (uint32)GLOBALS._nMsgs);
+ if (GLOBALS._hMsgs == NULL)
+ return false;
+
+ GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+#else
+ GLOBALS._lpmmMsgs=(LPMPALMSG)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MPALMSG) * (uint32)GLOBALS._nMsgs);
+ if (GLOBALS._lpmmMsgs==NULL)
+ return false;
+#endif
+
+ for (uint16 i = 0; i < GLOBALS._nMsgs; i++) {
+ GLOBALS._lpmmMsgs->_wNum = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ uint16 j;
+ for (j = 0; lpBuf[j] != 0;)
+ j += lpBuf[j] + 1;
+
+ GLOBALS._lpmmMsgs->_hText = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, j + 1);
+ lpTemp = (byte *)globalLock(GLOBALS._lpmmMsgs->_hText);
+
+ for (j = 0; lpBuf[j] != 0;) {
+ memcpy(lpTemp, &lpBuf[j + 1], lpBuf[j]);
+ lpTemp += lpBuf[j];
+ *lpTemp ++= '\0';
+ j += lpBuf[j] + 1;
+ }
+
+ lpBuf += j + 1;
+ *lpTemp = '\0';
+
+ globalUnlock(GLOBALS._lpmmMsgs->_hText);
+ GLOBALS._lpmmMsgs++;
+ }
+
+#ifdef NEED_LOCK_MSGS
+ globalUnlock(GLOBALS._hMsgs);
+#endif
+
+ // 3. Objects
+ if (lpBuf[0] != 'O' || lpBuf[1] != 'B' || lpBuf[2] != 'J' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nObjs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Check out the dialogs
+ GLOBALS._nDialogs = 0;
+ GLOBALS._hDialogs = GLOBALS._lpmdDialogs = NULL;
+ if (*((const byte *)lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Dialog", 6) == 0) {
+ GLOBALS._nDialogs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ GLOBALS._hDialogs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nDialogs * sizeof(MpalDialog));
+ if (GLOBALS._hDialogs == NULL)
+ return false;
+
+ GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+
+ for (uint16 i = 0; i < GLOBALS._nDialogs; i++) {
+ if ((lpBuf = parseDialog(lpBuf + 7, &GLOBALS._lpmdDialogs[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hDialogs);
+ }
+
+ // Check the items
+ GLOBALS._nItems = 0;
+ GLOBALS._hItems = GLOBALS._lpmiItems = NULL;
+ if (*(lpBuf + 2) == 4 && strncmp((const char *)lpBuf + 3, "Item", 4) == 0) {
+ GLOBALS._nItems = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory and read them in
+ GLOBALS._hItems = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nItems * sizeof(MpalItem));
+ if (GLOBALS._hItems == NULL)
+ return false;
+
+ GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+
+ for (uint16 i = 0; i < GLOBALS._nItems; i++) {
+ if ((lpBuf = parseItem(lpBuf + 5, &GLOBALS._lpmiItems[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hItems);
+ }
+
+ // Check the locations
+ GLOBALS._nLocations = 0;
+ GLOBALS._hLocations = GLOBALS._lpmlLocations = NULL;
+ if (*(lpBuf + 2) == 8 && strncmp((const char *)lpBuf + 3, "Location", 8) == 0) {
+ GLOBALS._nLocations = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory and read them in
+ GLOBALS._hLocations = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nLocations * sizeof(MpalLocation));
+ if (GLOBALS._hLocations == NULL)
+ return false;
+
+ GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations);
+
+ for (uint16 i = 0; i < GLOBALS._nLocations; i++) {
+ if ((lpBuf = ParseLocation(lpBuf + 9, &GLOBALS._lpmlLocations[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hLocations);
+ }
+
+ // Check the scripts
+ GLOBALS._nScripts = 0;
+ GLOBALS._hScripts = GLOBALS._lpmsScripts = NULL;
+ if (*(lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Script", 6) == 0) {
+ GLOBALS._nScripts = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory
+ GLOBALS._hScripts = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nScripts * sizeof(MpalScript));
+ if (GLOBALS._hScripts == NULL)
+ return false;
+
+ GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+
+ for (uint16 i = 0; i < GLOBALS._nScripts; i++) {
+ if ((lpBuf = ParseScript(lpBuf + 7, &GLOBALS._lpmsScripts[i])) == NULL)
+ return false;
+
+ // Sort the various moments of the script
+ //qsort(
+ //GLOBALS.lpmsScripts[i].Moment,
+ //GLOBALS.lpmsScripts[i].nMoments,
+ //sizeof(GLOBALS.lpmsScripts[i].Moment[0]),
+ //(int (*)(const void *, const void *))CompareMoments
+ //);
+ }
+
+ globalUnlock(GLOBALS._hScripts);
+ }
+
+ if (lpBuf[0] != 'E' || lpBuf[1] != 'N' || lpBuf[2] != 'D' || lpBuf[3] != '0')
+ return false;
+
+ return true;
+}
+
+/**
+ * Free the given dialog
+ */
+static void freeDialog(LpMpalDialog lpmdDialog) {
+ // Free the periods
+ for (int i = 0; i < MAX_PERIODS_PER_DIALOG && (lpmdDialog->_periods[i]); ++i)
+ globalFree(lpmdDialog->_periods[i]);
+
+ for (int i = 0; i < MAX_COMMANDS_PER_DIALOG && (lpmdDialog->_command[i]._type); i++) {
+ if (lpmdDialog->_command[i]._type == 2) {
+ // Variable assign
+ globalDestroy(lpmdDialog->_command[i]._lpszVarName);
+ freeExpression(lpmdDialog->_command[i]._expr);
+ }
+ }
+
+ // Free the choices
+ for (int i = 0; i < MAX_CHOICES_PER_DIALOG; ++i) {
+ for (int j = 0; j < MAX_SELECTS_PER_CHOICE; j++) {
+ if (lpmdDialog->_choice[i]._select[j]._when)
+ freeExpression(lpmdDialog->_choice[i]._select[j]._when);
+ }
+ }
+}
+
+/**
+ * Frees any data allocated from the parsing of the MPC file
+ */
+void freeMpc() {
+ // Free variables
+ globalFree(GLOBALS._hVars);
+
+ // Free messages
+ LpMpalMsg lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+ for (int i = 0; i < GLOBALS._nMsgs; i++, ++lpmmMsgs)
+ globalFree(lpmmMsgs->_hText);
+
+ globalUnlock(GLOBALS._hMsgs);
+ globalFree(GLOBALS._hMsgs);
+
+ // Free objects
+ if (GLOBALS._hDialogs) {
+ LpMpalDialog lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+
+ for (int i = 0; i < GLOBALS._nDialogs; i++, ++lpmdDialogs)
+ freeDialog(lpmdDialogs);
+
+ globalFree(GLOBALS._hDialogs);
+ }
+
+ // Free items
+ if (GLOBALS._hItems) {
+ LpMpalItem lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+
+ for (int i = 0; i < GLOBALS._nItems; ++i, ++lpmiItems)
+ freeItem(lpmiItems);
+
+ globalUnlock(GLOBALS._hItems);
+ globalFree(GLOBALS._hItems);
+ }
+
+ // Free the locations
+ if (GLOBALS._hLocations) {
+ globalFree(GLOBALS._hLocations);
+ }
+
+ // Free the scripts
+ if (GLOBALS._hScripts) {
+ LpMpalScript lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+
+ for (int i = 0; i < GLOBALS._nScripts; ++i, ++lpmsScripts) {
+ FreeScript(lpmsScripts);
+ }
+
+ globalUnlock(GLOBALS._hScripts);
+ }
+}
+
+//@}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/loadmpc.h b/engines/tony/mpal/loadmpc.h
new file mode 100644
index 0000000000..20956288aa
--- /dev/null
+++ b/engines/tony/mpal/loadmpc.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef __LOADMPC_H
+#define __LOADMPC_H
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Function prototypes
+\****************************************************************************/
+
+/**
+ * Reads and interprets the MPC file, and create structures for various directives
+ * in the global variables
+ *
+ * @param lpBuf Buffer containing the MPC file data, excluding the header.
+ * @returns True if succeeded OK, false if failure.
+ */
+bool parseMpc(const byte *lpBuf);
+
+/**
+ * Frees any data allocated from the parsing of the MPC file
+ */
+void freeMpc();
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/lzo.cpp b/engines/tony/mpal/lzo.cpp
new file mode 100644
index 0000000000..3d0751a5ca
--- /dev/null
+++ b/engines/tony/mpal/lzo.cpp
@@ -0,0 +1,511 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/* minilzo.c -- mini subset of the LZO real-time data compression library
+
+ This file is part of the LZO real-time data compression library.
+
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+ All Rights Reserved.
+
+ The LZO library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ The LZO library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the LZO library; see the file COPYING.
+ If not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ Markus F.X.J. Oberhumer
+ <markus@oberhumer.com>
+ http://www.oberhumer.com/opensource/lzo/
+ */
+
+#include "lzo.h"
+#include "common/textconsole.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+#define pd(a, b) ((uint32) ((a) - (b)))
+
+#define TEST_IP (ip < ip_end)
+#define TEST_OP 1
+#define NEED_IP(x) ((void) 0)
+#define NEED_OP(x) ((void) 0)
+#define TEST_LB(m_pos) ((void) 0)
+
+#define M2_MAX_OFFSET 0x0800
+#define LZO1X
+
+/**
+ * Decompresses an LZO compressed resource
+ */
+int lzo1x_decompress(const byte *in, uint32 in_len, byte *out, uint32 *out_len) {
+ register byte *op;
+ register const byte *ip;
+ register uint32 t = 0;
+#if defined(COPY_DICT)
+ uint32 m_off;
+ const byte *dict_end;
+#else
+ register const byte *m_pos;
+#endif
+
+ const byte * const ip_end = in + in_len;
+#if defined(HAVE_ANY_OP)
+ byte * const op_end = out + *out_len;
+#endif
+#if defined(LZO1Z)
+ uint32 last_m_off = 0;
+#endif
+
+#if defined(COPY_DICT)
+ if (dict)
+ {
+ if (dict_len > M4_MAX_OFFSET)
+ {
+ dict += dict_len - M4_MAX_OFFSET;
+ dict_len = M4_MAX_OFFSET;
+ }
+ dict_end = dict + dict_len;
+ }
+ else
+ {
+ dict_len = 0;
+ dict_end = NULL;
+ }
+#endif
+
+ *out_len = 0;
+
+ op = out;
+ ip = in;
+
+ if (*ip > 17)
+ {
+ t = *ip++ - 17;
+ if (t < 4)
+ goto match_next;
+ assert(t > 0); NEED_OP(t); NEED_IP(t+1);
+ do *op++ = *ip++; while (--t > 0);
+ goto first_literal_run;
+ }
+
+ while (TEST_IP && TEST_OP)
+ {
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 15 + *ip++;
+ }
+ assert(t > 0); NEED_OP(t+3); NEED_IP(t+4);
+#if defined(LZO_UNALIGNED_OK_8) && defined(LZO_UNALIGNED_OK_4)
+ t += 3;
+ if (t >= 8) do
+ {
+ UA_COPY64(op, ip);
+ op += 8; ip += 8; t -= 8;
+ } while (t >= 8);
+ if (t >= 4)
+ {
+ UA_COPY32(op, ip);
+ op += 4; ip += 4; t -= 4;
+ }
+ if (t > 0)
+ {
+ *op++ = *ip++;
+ if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } }
+ }
+#elif defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+#if !defined(LZO_UNALIGNED_OK_4)
+ if (PTR_ALIGNED2_4(op, ip))
+ {
+#endif
+ UA_COPY32(op, ip);
+ op += 4; ip += 4;
+ if (--t > 0)
+ {
+ if (t >= 4)
+ {
+ do {
+ UA_COPY32(op, ip);
+ op += 4; ip += 4; t -= 4;
+ } while (t >= 4);
+ if (t > 0) do *op++ = *ip++; while (--t > 0);
+ }
+ else
+ do *op++ = *ip++; while (--t > 0);
+ }
+#if !defined(LZO_UNALIGNED_OK_4)
+ }
+ else
+#endif
+#endif
+#if !defined(LZO_UNALIGNED_OK_4) && !defined(LZO_UNALIGNED_OK_8)
+ {
+ *op++ = *ip++; *op++ = *ip++; *op++ = *ip++;
+ do *op++ = *ip++; while (--t > 0);
+ }
+#endif
+
+first_literal_run:
+
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+#else
+ m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2);
+#endif
+ NEED_OP(3);
+ t = 3; COPY_DICT(t, m_off)
+#else
+#if defined(LZO1Z)
+ t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+ m_pos = op - t;
+ last_m_off = t;
+#else
+ m_pos = op - (1 + M2_MAX_OFFSET);
+ m_pos -= t >> 2;
+ m_pos -= *ip++ << 2;
+#endif
+ TEST_LB(m_pos); NEED_OP(3);
+ *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos;
+#endif
+ goto match_done;
+
+ do {
+match:
+ if (t >= 64)
+ {
+#if defined(COPY_DICT)
+#if defined(LZO1X)
+ m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3);
+ t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+ m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2);
+ t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+ m_off = t & 0x1f;
+ if (m_off >= 0x1c)
+ m_off = last_m_off;
+ else
+ {
+ m_off = 1 + (m_off << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+ }
+ t = (t >> 5) - 1;
+#endif
+#else
+#if defined(LZO1X)
+ m_pos = op - 1;
+ m_pos -= (t >> 2) & 7;
+ m_pos -= *ip++ << 3;
+ t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+ m_pos = op - 1;
+ m_pos -= (t >> 2) & 3;
+ m_pos -= *ip++ << 2;
+ t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+ {
+ uint32 off = t & 0x1f;
+ m_pos = op;
+ if (off >= 0x1c)
+ {
+ assert(last_m_off > 0);
+ m_pos -= last_m_off;
+ }
+ else
+ {
+ off = 1 + (off << 6) + (*ip++ >> 2);
+ m_pos -= off;
+ last_m_off = off;
+ }
+ }
+ t = (t >> 5) - 1;
+#endif
+ TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1);
+ goto copy_match;
+#endif
+ }
+ else if (t >= 32)
+ {
+ t &= 31;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 31 + *ip++;
+ }
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+ last_m_off = m_off;
+#else
+ m_off = 1 + (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#else
+#if defined(LZO1Z)
+ {
+ uint32 off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+ m_pos = op - off;
+ last_m_off = off;
+ }
+#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN)
+ m_pos = op - 1;
+ m_pos -= UA_GET16(ip) >> 2;
+#else
+ m_pos = op - 1;
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#endif
+ ip += 2;
+ }
+ else if (t >= 16)
+ {
+#if defined(COPY_DICT)
+ m_off = (t & 8) << 11;
+#else
+ m_pos = op;
+ m_pos -= (t & 8) << 11;
+#endif
+ t &= 7;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 7 + *ip++;
+ }
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off += (ip[0] << 6) + (ip[1] >> 2);
+#else
+ m_off += (ip[0] >> 2) + (ip[1] << 6);
+#endif
+ ip += 2;
+ if (m_off == 0)
+ goto eof_found;
+ m_off += 0x4000;
+#if defined(LZO1Z)
+ last_m_off = m_off;
+#endif
+#else
+#if defined(LZO1Z)
+ m_pos -= (ip[0] << 6) + (ip[1] >> 2);
+#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN)
+ m_pos -= UA_GET16(ip) >> 2;
+#else
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+ ip += 2;
+ if (m_pos == op)
+ goto eof_found;
+ m_pos -= 0x4000;
+#if defined(LZO1Z)
+ last_m_off = pd((const byte *)op, m_pos);
+#endif
+#endif
+ }
+ else
+ {
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = 1 + (t << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+#else
+ m_off = 1 + (t >> 2) + (*ip++ << 2);
+#endif
+ NEED_OP(2);
+ t = 2; COPY_DICT(t, m_off)
+#else
+#if defined(LZO1Z)
+ t = 1 + (t << 6) + (*ip++ >> 2);
+ m_pos = op - t;
+ last_m_off = t;
+#else
+ m_pos = op - 1;
+ m_pos -= t >> 2;
+ m_pos -= *ip++ << 2;
+#endif
+ TEST_LB(m_pos); NEED_OP(2);
+ *op++ = *m_pos++; *op++ = *m_pos;
+#endif
+ goto match_done;
+ }
+
+#if defined(COPY_DICT)
+
+ NEED_OP(t+3-1);
+ t += 3-1; COPY_DICT(t, m_off)
+
+#else
+
+ TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1);
+#if defined(LZO_UNALIGNED_OK_8) && defined(LZO_UNALIGNED_OK_4)
+ if (op - m_pos >= 8)
+ {
+ t += (3 - 1);
+ if (t >= 8) do
+ {
+ UA_COPY64(op, m_pos);
+ op += 8; m_pos += 8; t -= 8;
+ } while (t >= 8);
+ if (t >= 4)
+ {
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4;
+ }
+ if (t > 0)
+ {
+ *op++ = m_pos[0];
+ if (t > 1) { *op++ = m_pos[1]; if (t > 2) { *op++ = m_pos[2]; } }
+ }
+ }
+ else
+#elif defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+#if !defined(LZO_UNALIGNED_OK_4)
+ if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op, m_pos))
+ {
+ assert((op - m_pos) >= 4);
+#else
+ if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4)
+ {
+#endif
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4 - (3 - 1);
+ do {
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4;
+ } while (t >= 4);
+ if (t > 0) do *op++ = *m_pos++; while (--t > 0);
+ }
+ else
+#endif
+ {
+copy_match:
+ *op++ = *m_pos++; *op++ = *m_pos++;
+ do *op++ = *m_pos++; while (--t > 0);
+ }
+
+#endif
+
+match_done:
+#if defined(LZO1Z)
+ t = ip[-1] & 3;
+#else
+ t = ip[-2] & 3;
+#endif
+ if (t == 0)
+ break;
+
+match_next:
+ assert(t > 0); assert(t < 4); NEED_OP(t); NEED_IP(t+1);
+#if 0
+ do *op++ = *ip++; while (--t > 0);
+#else
+ *op++ = *ip++;
+ if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } }
+#endif
+ t = *ip++;
+ } while (TEST_IP && TEST_OP);
+ }
+
+#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP)
+ *out_len = pd(op, out);
+ return LZO_E_EOF_NOT_FOUND;
+#endif
+
+eof_found:
+ assert(t == 1);
+ *out_len = pd(op, out);
+ return (ip == ip_end ? LZO_E_OK :
+ (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+
+#if defined(HAVE_NEED_IP)
+input_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_INPUT_OVERRUN;
+#endif
+
+#if defined(HAVE_NEED_OP)
+output_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_OUTPUT_OVERRUN;
+#endif
+
+#if defined(LZO_TEST_OVERRUN_LOOKBEHIND)
+lookbehind_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_LOOKBEHIND_OVERRUN;
+#endif
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/lzo.h b/engines/tony/mpal/lzo.h
new file mode 100644
index 0000000000..ebb1c4b516
--- /dev/null
+++ b/engines/tony/mpal/lzo.h
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/* minilzo.c -- mini subset of the LZO real-time data compression library
+
+ This file is part of the LZO real-time data compression library.
+
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+ All Rights Reserved.
+
+ The LZO library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ The LZO library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the LZO library; see the file COPYING.
+ If not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ Markus F.X.J. Oberhumer
+ <markus@oberhumer.com>
+ http://www.oberhumer.com/opensource/lzo/
+ */
+
+#ifndef TONY_MPAL_LZO_H
+#define TONY_MPAL_LZO_H
+
+#include "common/scummsys.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/* Error codes for the compression/decompression functions. Negative
+ * values are errors, positive values will be used for special but
+ * normal events.
+ */
+#define LZO_E_OK 0
+#define LZO_E_ERROR (-1)
+#define LZO_E_OUT_OF_MEMORY (-2) /* [lzo_alloc_func_t failure] */
+#define LZO_E_NOT_COMPRESSIBLE (-3) /* [not used right now] */
+#define LZO_E_INPUT_OVERRUN (-4)
+#define LZO_E_OUTPUT_OVERRUN (-5)
+#define LZO_E_LOOKBEHIND_OVERRUN (-6)
+#define LZO_E_EOF_NOT_FOUND (-7)
+#define LZO_E_INPUT_NOT_CONSUMED (-8)
+#define LZO_E_NOT_YET_IMPLEMENTED (-9) /* [not used right now] */
+#define LZO_E_INVALID_ARGUMENT (-10)
+
+#define LZO1X_999_MEM_COMPRESS ((uint32) (14 * 16384L * sizeof(uint16)))
+
+/**
+ * Decompresses an LZO compressed resource
+ */
+int lzo1x_decompress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len);
+
+/**
+ * Comrpess a data block into an LZO stream
+ */
+int lzo1x_1_compress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len, void *wrkmem);
+
+/**
+ * better compression ratio at the cost of more memory and time
+ */
+int lzo1x_999_compress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len, void *wrkmem);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif /* already included */
diff --git a/engines/tony/mpal/memory.cpp b/engines/tony/mpal/memory.cpp
new file mode 100644
index 0000000000..dfbf16e789
--- /dev/null
+++ b/engines/tony/mpal/memory.cpp
@@ -0,0 +1,127 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#include "common/algorithm.h"
+#include "common/textconsole.h"
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* MemoryManager methods
+\****************************************************************************/
+
+/**
+ * Allocates a new memory block
+ * @return Returns a MemoryItem instance for the new block
+ */
+MpalHandle MemoryManager::allocate(uint32 size, uint flags) {
+ MemoryItem *newItem = (MemoryItem *)malloc(sizeof(MemoryItem) + size);
+ newItem->_id = BLOCK_ID;
+ newItem->_size = size;
+ newItem->_lockCount = 0;
+
+ // If requested, clear the allocated data block
+ if ((flags & GMEM_ZEROINIT) != 0) {
+ byte *dataP = newItem->_data;
+ Common::fill(dataP, dataP + size, 0);
+ }
+
+ return (MpalHandle)newItem;
+}
+
+/**
+ * Allocates a new memory block and returns its data pointer
+ * @return Data pointer to allocated block
+ */
+void *MemoryManager::alloc(uint32 size, uint flags) {
+ MemoryItem *item = (MemoryItem *)allocate(size, flags);
+ ++item->_lockCount;
+ return &item->_data[0];
+}
+
+#define OFFSETOF(type, field) ((size_t) &(((type *) 0)->field))
+
+/**
+ * Returns a reference to the MemoryItem for a gien byte pointer
+ * @param block Byte pointer
+ */
+MemoryItem *MemoryManager::getItem(MpalHandle handle) {
+ MemoryItem *rec = (MemoryItem *)((byte *)handle - OFFSETOF(MemoryItem, _data));
+ assert(rec->_id == BLOCK_ID);
+ return rec;
+}
+
+/**
+ * Returns a size of a memory block given its pointer
+ */
+uint32 MemoryManager::getSize(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ return item->_size;
+}
+
+/**
+ * Erases a given item
+ */
+void MemoryManager::freeBlock(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ free(item);
+}
+
+/**
+ * Erases a given item
+ */
+void MemoryManager::destroyItem(MpalHandle handle) {
+ MemoryItem *item = getItem(handle);
+ assert(item->_id == BLOCK_ID);
+ free(item);
+}
+
+/**
+ * Locks an item for access
+ */
+byte *MemoryManager::lockItem(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ ++item->_lockCount;
+ return &item->_data[0];
+}
+
+/**
+ * Unlocks a locked item
+ */
+void MemoryManager::unlockItem(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ assert(item->_lockCount > 0);
+ --item->_lockCount;
+}
+
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/memory.h b/engines/tony/mpal/memory.h
new file mode 100644
index 0000000000..9c21cc20e6
--- /dev/null
+++ b/engines/tony/mpal/memory.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef TONY_MPAL_MEMORY
+#define TONY_MPAL_MEMORY
+
+#include "common/scummsys.h"
+#include "common/list.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+typedef void *MpalHandle;
+
+struct MemoryItem {
+ uint32 _id;
+ uint32 _size;
+ int _lockCount;
+ byte _data[1];
+
+ // Casting for access to data
+ operator void *() { return &_data[0]; }
+};
+
+class MemoryManager {
+private:
+ static MemoryItem *getItem(MpalHandle handle);
+public:
+ static MpalHandle allocate(uint32 size, uint flags);
+ static void *alloc(uint32 size, uint flags);
+ static void freeBlock(MpalHandle handle);
+ static void destroyItem(MpalHandle handle);
+ static uint32 getSize(MpalHandle handle);
+ static byte *lockItem(MpalHandle handle);
+ static void unlockItem(MpalHandle handle);
+};
+
+// defines
+#define globalAlloc(flags, size) MemoryManager::alloc(size, flags)
+#define globalAllocate(flags, size) MemoryManager::allocate(size, flags)
+#define globalFree(handle) MemoryManager::freeBlock(handle)
+#define globalDestroy(handle) MemoryManager::destroyItem(handle)
+#define globalLock(handle) MemoryManager::lockItem(handle)
+#define globalUnlock(handle) MemoryManager::unlockItem(handle)
+#define globalSize(handle) MemoryManager::getSize(handle)
+
+#define GMEM_FIXED 1
+#define GMEM_MOVEABLE 2
+#define GMEM_ZEROINIT 4
+
+const uint32 BLOCK_ID = 0x12345678;
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
diff --git a/engines/tony/mpal/mpal.cpp b/engines/tony/mpal/mpal.cpp
new file mode 100644
index 0000000000..8d83363c24
--- /dev/null
+++ b/engines/tony/mpal/mpal.cpp
@@ -0,0 +1,2089 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "tony/tony.h"
+#include "tony/mpal/lzo.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/mpaldll.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Internal functions
+\****************************************************************************/
+
+/**
+ * Locks the variables for access
+ */
+void lockVar() {
+ GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars);
+}
+
+/**
+ * Unlocks variables after use
+ */
+void unlockVar() {
+ globalUnlock(GLOBALS._hVars);
+}
+
+/**
+ * Locks the messages for access
+ */
+static void LockMsg() {
+#ifdef NEED_LOCK_MSGS
+ GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+#endif
+}
+
+/**
+ * Unlocks the messages after use
+ */
+static void UnlockMsg() {
+#ifdef NEED_LOCK_MSGS
+ globalUnlock(GLOBALS._hMsgs);
+#endif
+}
+
+/**
+ * Locks the dialogs for access
+ */
+static void lockDialogs() {
+ GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+}
+
+/**
+ * Unlocks the dialogs after use
+ */
+static void unlockDialogs() {
+ globalUnlock(GLOBALS._hDialogs);
+}
+
+/**
+ * Locks the location data structures for access
+ */
+static void lockLocations() {
+ GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations);
+}
+
+/**
+ * Unlocks the location structures after use
+ */
+static void unlockLocations() {
+ globalUnlock(GLOBALS._hLocations);
+}
+
+/**
+ * Locks the items structures for use
+ */
+static void lockItems() {
+ GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+}
+
+/**
+ * Unlocks the items structures after use
+ */
+static void unlockItems() {
+ globalUnlock(GLOBALS._hItems);
+}
+
+/**
+ * Locks the script data structures for use
+ */
+static void LockScripts() {
+ GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+}
+
+/**
+ * Unlocks the script data structures after use
+ */
+static void unlockScripts() {
+ globalUnlock(GLOBALS._hScripts);
+}
+
+/**
+ * Returns the current value of a global variable
+ *
+ * @param lpszVarName Name of the variable
+ * @returns Current value
+ * @remarks Before using this method, you must call lockVar() to
+ * lock the global variablves for use. Then afterwards, you will
+ * need to remember to call UnlockVar()
+ */
+int32 varGetValue(const char *lpszVarName) {
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ for (int i = 0; i < GLOBALS._nVars; v++, i++)
+ if (strcmp(lpszVarName, v->_lpszVarName) == 0)
+ return v->_dwVal;
+
+ GLOBALS._mpalError = 1;
+ return 0;
+}
+
+/**
+ * Sets the value of a MPAL global variable
+ * @param lpszVarName Name of the variable
+ * @param val Value to set
+ */
+void varSetValue(const char *lpszVarName, int32 val) {
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ for (uint i = 0; i < GLOBALS._nVars; v++, i++)
+ if (strcmp(lpszVarName, v->_lpszVarName) == 0) {
+ v->_dwVal = val;
+ if (GLOBALS._lpiifCustom != NULL && strncmp(v->_lpszVarName, "Pattern.", 8) == 0) {
+ i = 0;
+ sscanf(v->_lpszVarName, "Pattern.%u", &i);
+ GLOBALS._lpiifCustom(i, val, -1);
+ } else if (GLOBALS._lpiifCustom != NULL && strncmp(v->_lpszVarName, "Status.", 7) == 0) {
+ i = 0;
+ sscanf(v->_lpszVarName,"Status.%u", &i);
+ GLOBALS._lpiifCustom(i, -1, val);
+ }
+ return;
+ }
+
+ GLOBALS._mpalError = 1;
+ return;
+}
+
+/**
+ * Find the index of a location within the location array. Remember to call LockLoc() beforehand.
+ *
+ * @param nLoc Location number to search for
+ * @returns Index, or -1 if the location is not present
+ * @remarks This function requires the location list to have
+ * first been locked with a call to LockLoc().
+ */
+static int locGetOrderFromNum(uint32 nLoc) {
+ LpMpalLocation loc = GLOBALS._lpmlLocations;
+
+ for (int i = 0; i < GLOBALS._nLocations; i++, loc++)
+ if (loc->_nObj == nLoc)
+ return i;
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a message within the messages array
+ * @param nMsg Message number to search for
+ * @returns Index, or -1 if the message is not present
+ * @remarks This function requires the message list to have
+ * first been locked with a call to LockMsg()
+ */
+static int msgGetOrderFromNum(uint32 nMsg) {
+ LpMpalMsg msg = GLOBALS._lpmmMsgs;
+
+ for (int i = 0; i < GLOBALS._nMsgs; i++, msg++) {
+ if (msg->_wNum == nMsg)
+ return i;
+ }
+
+ return -1;
+}
+
+/**
+ * Find the index of an item within the items array
+ * @param nItem Item number to search for
+ * @returns Index, or -1 if the item is not present
+ * @remarks This function requires the item list to have
+ * first been locked with a call to LockItems()
+ */
+static int itemGetOrderFromNum(uint32 nItem) {
+ LpMpalItem item = GLOBALS._lpmiItems;
+
+ for (int i = 0; i < GLOBALS._nItems; i++, item++) {
+ if (item->_nObj == nItem)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a script within the scripts array
+ * @param nScript Script number to search for
+ * @returns Index, or -1 if the script is not present
+ * @remarks This function requires the script list to have
+ * first been locked with a call to LockScripts()
+ */
+static int scriptGetOrderFromNum(uint32 nScript) {
+ LpMpalScript script = GLOBALS._lpmsScripts;
+
+ for (int i = 0; i < GLOBALS._nScripts; i++, script++) {
+ if (script->_nObj == nScript)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a dialog within the dialogs array
+ * @param nDialog Dialog number to search for
+ * @returns Index, or -1 if the dialog is not present
+ * @remarks This function requires the dialog list to have
+ * first been locked with a call to LockDialogs()
+ */
+static int dialogGetOrderFromNum(uint32 nDialog) {
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs;
+
+ for (int i = 0; i < GLOBALS._nDialogs; i++, dialog++) {
+ if (dialog->_nObj == nDialog)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Duplicates a message
+ * @param nMsgOrd Index of the message inside the messages array
+ * @returns Pointer to the duplicated message.
+ * @remarks Remember to free the duplicated message when done with it.
+ */
+static char *DuplicateMessage(uint32 nMsgOrd) {
+ const char *origmsg;
+ char *clonemsg;
+
+ if (nMsgOrd == (uint32)-1)
+ return NULL;
+
+ origmsg = (const char *)globalLock(GLOBALS._lpmmMsgs[nMsgOrd]._hText);
+
+ int j = 0;
+ while (origmsg[j] != '\0' || origmsg[j + 1] != '\0')
+ j++;
+ j += 2;
+
+ clonemsg = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, j);
+ if (clonemsg == NULL)
+ return NULL;
+
+ memcpy(clonemsg, origmsg, j);
+ globalUnlock(GLOBALS._lpmmMsgs[nMsgOrd]._hText);
+
+ return clonemsg;
+}
+
+
+/**
+ * Duplicate a sentence of a dialog
+ * @param nDlgOrd Index of the dialog in the dialogs array
+ * @param nPeriod Sentence number to be duplicated.
+ * @returns Pointer to the duplicated phrase. Remember to free it
+ * when done with it.
+ */
+static char *duplicateDialogPeriod(uint32 nPeriod) {
+ const char *origmsg;
+ char *clonemsg;
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ for (int j = 0; dialog->_periods[j] != NULL; j++) {
+ if (dialog->_periodNums[j] == nPeriod) {
+ // Found the phrase, it should be duplicated
+ origmsg = (const char *)globalLock(dialog->_periods[j]);
+
+ // Calculate the length and allocate memory
+ int i = 0;
+ while (origmsg[i] != '\0')
+ i++;
+
+ clonemsg = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, i + 1);
+ if (clonemsg == NULL)
+ return NULL;
+
+ memcpy(clonemsg, origmsg, i);
+
+ globalUnlock(dialog->_periods[j]);
+
+ return clonemsg;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Load a resource from the MPR file
+ *
+ * @param dwId ID of the resource to load
+ * @returns Handle to the loaded resource
+ */
+MpalHandle resLoad(uint32 dwId) {
+ MpalHandle h;
+ char head[4];
+ byte *temp, *buf;
+
+ for (int i = 0; i < GLOBALS._nResources; i++)
+ if (GLOBALS._lpResources[i * 2] == dwId) {
+ GLOBALS._hMpr.seek(GLOBALS._lpResources[i * 2 + 1]);
+ uint32 nBytesRead = GLOBALS._hMpr.read(head, 4);
+ if (nBytesRead != 4)
+ return NULL;
+ if (head[0] != 'R' || head[1] != 'E' || head[2] != 'S' || head[3] != 'D')
+ return NULL;
+
+ uint32 nSizeDecomp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return NULL;
+
+ uint32 nSizeComp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return NULL;
+
+ h = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, nSizeDecomp + (nSizeDecomp / 1024) * 16);
+ buf = (byte *)globalLock(h);
+ temp = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, nSizeComp);
+
+ nBytesRead = GLOBALS._hMpr.read(temp, nSizeComp);
+ if (nBytesRead != nSizeComp)
+ return NULL;
+
+ lzo1x_decompress(temp, nSizeComp, buf, &nBytesRead);
+ if (nBytesRead != nSizeDecomp)
+ return NULL;
+
+ globalDestroy(temp);
+ globalUnlock(h);
+ return h;
+ }
+
+ return NULL;
+}
+
+static uint32 *getSelectList(uint32 i) {
+ uint32 *sl;
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Count how many are active selects
+ int num = 0;
+ for (int j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._curActive)
+ num++;
+ }
+
+ // If there are 0, it's a mistake
+ if (num == 0)
+ return NULL;
+
+ sl = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(uint32) * (num + 1));
+ if (sl == NULL)
+ return NULL;
+
+ // Copy all the data inside the active select list
+ int k = 0;
+ for (int j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._curActive)
+ sl[k++] = dialog->_choice[i]._select[j]._dwData;
+ }
+
+ sl[k] = (uint32)NULL;
+ return sl;
+}
+
+static uint32 *GetItemList(uint32 nLoc) {
+ uint32 *il;
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ uint32 num = 0;
+ for (uint32 i = 0; i < GLOBALS._nVars; i++, v++) {
+ if (strncmp(v->_lpszVarName, "Location", 8) == 0 && v->_dwVal == nLoc)
+ num++;
+ }
+
+ il = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(uint32) * (num + 1));
+ if (il == NULL)
+ return NULL;
+
+ v = GLOBALS._lpmvVars;
+ uint32 j = 0;
+ for (uint32 i = 0; i < GLOBALS._nVars; i++, v++) {
+ if (strncmp(v->_lpszVarName, "Location", 8) == 0 && v->_dwVal == nLoc) {
+ sscanf(v->_lpszVarName, "Location.%u", &il[j]);
+ j++;
+ }
+ }
+
+ il[j] = (uint32)NULL;
+ return il;
+}
+
+static LpItem getItemData(uint32 nOrdItem) {
+ LpMpalItem curitem = GLOBALS._lpmiItems + nOrdItem;
+ char *dat;
+ char *patlength;
+
+ // Zeroing out the allocated memory is required!!!
+ LpItem ret = (LpItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(Item));
+ if (ret == NULL)
+ return NULL;
+ ret->_speed = 150;
+
+ MpalHandle hDat = resLoad(curitem->_dwRes);
+ dat = (char *)globalLock(hDat);
+
+ if (dat[0] == 'D' && dat[1] == 'A' && dat[2] == 'T') {
+ int i = dat[3]; // For version 1.0!!
+ dat += 4;
+
+ if (i >= 0x10) { // From 1.0, there's a destination point for each object
+ ret->_destX = (int16)READ_LE_UINT16(dat);
+ ret->_destY = (int16)READ_LE_UINT16(dat + 2);
+ dat += 4;
+ }
+
+ if (i >= 0x11) { // From 1.1, there's animation speed
+ ret->_speed = READ_LE_UINT16(dat);
+ dat += 2;
+ } else
+ ret->_speed = 150;
+ }
+
+ ret->_numframe = *dat++;
+ ret->_numpattern = *dat++;
+ ret->_destZ = *dat++;
+
+ // Upload the left & top co-ordinates of each frame
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_frameslocations[i].left = (int16)READ_LE_UINT16(dat);
+ ret->_frameslocations[i].top = (int16)READ_LE_UINT16(dat + 2);
+ dat += 4;
+ }
+
+ // Upload the size of each frame and calculate the right & bottom
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_frameslocations[i].right = (int16)READ_LE_UINT16(dat) + ret->_frameslocations[i].left;
+ ret->_frameslocations[i].bottom = (int16)READ_LE_UINT16(dat + 2) + ret->_frameslocations[i].top;
+ dat += 4;
+ }
+
+ // Upload the bounding boxes of each frame
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_bbox[i].left = (int16)READ_LE_UINT16(dat);
+ ret->_bbox[i].top = (int16)READ_LE_UINT16(dat + 2);
+ ret->_bbox[i].right = (int16)READ_LE_UINT16(dat + 4);
+ ret->_bbox[i].bottom = (int16)READ_LE_UINT16(dat + 6);
+ dat += 8;
+ }
+
+ // Load the animation pattern
+ patlength = dat;
+ dat += ret->_numpattern;
+
+ for (int i = 1; i < ret->_numpattern; i++) {
+ for (int j = 0; j < patlength[i]; j++)
+ ret->_pattern[i][j] = dat[j];
+ ret->_pattern[i][(int)patlength[i]] = 255; // Terminate pattern
+ dat += patlength[i];
+ }
+
+ // Upload the individual frames of animations
+ for (int i = 1; i < ret->_numframe; i++) {
+ uint32 dim = (uint32)(ret->_frameslocations[i].right - ret->_frameslocations[i].left) *
+ (uint32)(ret->_frameslocations[i].bottom - ret->_frameslocations[i].top);
+ ret->_frames[i] = (char *)globalAlloc(GMEM_FIXED, dim);
+
+ if (ret->_frames[i] == NULL)
+ return NULL;
+ memcpy(ret->_frames[i], dat, dim);
+ dat += dim;
+ }
+
+ // Check if we've got to the end of the file
+ int i = READ_LE_UINT16(dat);
+ if (i != 0xABCD)
+ return NULL;
+
+ globalUnlock(hDat);
+ globalFree(hDat);
+
+ return ret;
+}
+
+
+/**
+ * Thread that calls a custom function. It is used in scripts, so that each script
+ * function is executed without delaying the others.
+ *
+ * @param param pointer to a pointer to the structure that defines the call.
+ * @remarks The passed structure is freed when the process finishes.
+ */
+void CustomThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ LpCfCall p;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->p = *(const LpCfCall *)param;
+
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->p->_nCf], _ctx->p->_arg1, _ctx->p->_arg2, _ctx->p->_arg3, _ctx->p->_arg4);
+
+ globalFree(_ctx->p);
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Main process for running a script.
+ *
+ * @param param Pointer to a pointer to a structure containing the script data.
+ * @remarks The passed structure is freed when the process finishes.
+ */
+void ScriptThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ uint i, j, k;
+ uint32 dwStartTime;
+ uint32 dwCurTime;
+ uint32 dwId;
+ int numHandles;
+ LpCfCall p;
+ CORO_END_CONTEXT(_ctx);
+
+ static uint32 cfHandles[MAX_COMMANDS_PER_MOMENT];
+ LpMpalScript s = *(const LpMpalScript *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->dwStartTime = g_vm->getTime();
+ _ctx->numHandles = 0;
+
+// debugC(DEBUG_BASIC, kTonyDebugMPAL, "PlayScript(): Moments: %u\n", s->_nMoments);
+ for (_ctx->i = 0; _ctx->i < s->_nMoments; _ctx->i++) {
+ // Sleep for the required time
+ if (s->_moment[_ctx->i]._dwTime == -1) {
+ CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, _ctx->numHandles, cfHandles, true, CORO_INFINITE);
+ _ctx->dwStartTime = g_vm->getTime();
+ } else {
+ _ctx->dwCurTime = g_vm->getTime();
+ if (_ctx->dwCurTime < _ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime * 100)) {
+ // debugC(DEBUG_BASIC, kTonyDebugMPAL, "PlayScript(): Sleeping %lums\n",_ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime*100) - _ctx->dwCurTime);
+ CORO_INVOKE_1(CoroScheduler.sleep, _ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime * 100) - _ctx->dwCurTime);
+ }
+ }
+
+ _ctx->numHandles = 0;
+ for (_ctx->j = 0; _ctx->j < s->_moment[_ctx->i]._nCmds; _ctx->j++) {
+ _ctx->k = s->_moment[_ctx->i]._cmdNum[_ctx->j];
+
+ if (s->_command[_ctx->k]._type == 1) {
+ _ctx->p = (LpCfCall)globalAlloc(GMEM_FIXED, sizeof(CfCall));
+ if (_ctx->p == NULL) {
+ GLOBALS._mpalError = 1;
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ _ctx->p->_nCf = s->_command[_ctx->k]._nCf;
+ _ctx->p->_arg1 = s->_command[_ctx->k]._arg1;
+ _ctx->p->_arg2 = s->_command[_ctx->k]._arg2;
+ _ctx->p->_arg3 = s->_command[_ctx->k]._arg3;
+ _ctx->p->_arg4 = s->_command[_ctx->k]._arg4;
+
+ // !!! New process management
+ if ((cfHandles[_ctx->numHandles++] = CoroScheduler.createProcess(CustomThread, &_ctx->p, sizeof(LpCfCall))) == 0) {
+ GLOBALS._mpalError = 1;
+
+ CORO_KILL_SELF();
+ return;
+ }
+ } else if (s->_command[_ctx->k]._type == 2) {
+ lockVar();
+ varSetValue(
+ s->_command[_ctx->k]._lpszVarName,
+ evaluateExpression(s->_command[_ctx->k]._expr)
+ );
+ unlockVar();
+
+ } else {
+ GLOBALS._mpalError = 1;
+ globalFree(s);
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // WORKAROUND: Wait for events to pulse.
+ CORO_SLEEP(1);
+ }
+ }
+
+ globalFree(s);
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Thread that performs an action on an item. the thread always executes the action,
+ * so it should create a new item in which the action is the one required.
+ * Furthermore, the expression is not checked, but it is always performed the action.
+ *
+ * @param param Pointer to a pointer to a structure containing the action.
+ */
+void ActionThread(CORO_PARAM, const void *param) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ int j, k;
+ LpMpalItem item;
+
+ ~CoroContextTag() {
+ if (item)
+ globalDestroy(item);
+ }
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // The ActionThread owns the data block pointed to, so we need to make sure it's
+ // freed when the process exits
+ _ctx->item = *(const LpMpalItem *)param;
+
+ GLOBALS._mpalError = 0;
+ for (_ctx->j = 0; _ctx->j < _ctx->item->_action[_ctx->item->_dwRes]._nCmds; _ctx->j++) {
+ _ctx->k = _ctx->item->_action[_ctx->item->_dwRes]._cmdNum[_ctx->j];
+
+ if (_ctx->item->_command[_ctx->k]._type == 1) {
+ // Custom function
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Call=%s params=%d,%d,%d,%d",
+ CoroScheduler.getCurrentPID(), GLOBALS._lplpFunctionStrings[_ctx->item->_command[_ctx->k]._nCf].c_str(),
+ _ctx->item->_command[_ctx->k]._arg1, _ctx->item->_command[_ctx->k]._arg2,
+ _ctx->item->_command[_ctx->k]._arg3, _ctx->item->_command[_ctx->k]._arg4
+ );
+
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->item->_command[_ctx->k]._nCf],
+ _ctx->item->_command[_ctx->k]._arg1,
+ _ctx->item->_command[_ctx->k]._arg2,
+ _ctx->item->_command[_ctx->k]._arg3,
+ _ctx->item->_command[_ctx->k]._arg4
+
+ );
+ } else if (_ctx->item->_command[_ctx->k]._type == 2) {
+ // Variable assign
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Variable=%s",
+ CoroScheduler.getCurrentPID(), _ctx->item->_command[_ctx->k]._lpszVarName);
+
+ lockVar();
+ varSetValue(_ctx->item->_command[_ctx->k]._lpszVarName, evaluateExpression(_ctx->item->_command[_ctx->k]._expr));
+ unlockVar();
+
+ } else {
+ GLOBALS._mpalError = 1;
+ break;
+ }
+
+ // WORKAROUND: Wait for events to pulse.
+ CORO_SLEEP(1);
+ }
+
+ globalDestroy(_ctx->item);
+ _ctx->item = NULL;
+
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d ended", CoroScheduler.getCurrentPID());
+
+ CORO_END_CODE;
+}
+
+/**
+ * This thread monitors a created action to detect when it ends.
+ * @remarks Since actions can spawn sub-actions, this needs to be a
+ * separate thread to determine when the outer action is done
+ */
+void ShutUpActionThread(CORO_PARAM, const void *param) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ int slotNumber;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 pid = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE);
+
+ GLOBALS._bExecutingAction = false;
+
+ if (g_vm->_initialLoadSlotNumber != -1) {
+ _ctx->slotNumber = g_vm->_initialLoadSlotNumber;
+ g_vm->_initialLoadSlotNumber = -1;
+
+ CORO_INVOKE_1(g_vm->loadState, _ctx->slotNumber);
+ }
+
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Polls one location (starting point of a process)
+ *
+ * @param param Pointer to an index in the array of polling locations.
+ */
+void LocationPollThread(CORO_PARAM, const void *param) {
+ typedef struct {
+ uint32 _nItem, _nAction;
+
+ uint16 _wTime;
+ byte _perc;
+ MpalHandle _when;
+ byte _nCmds;
+ uint16 _cmdNum[MAX_COMMANDS_PER_ACTION];
+ uint32 _dwLastTime;
+ } MYACTION;
+
+ typedef struct {
+ uint32 _nItem;
+ uint32 _hThread;
+ } MYTHREAD;
+
+ CORO_BEGIN_CONTEXT;
+ uint32 *il;
+ int i, j, k;
+ int numitems;
+ int nRealItems;
+ LpMpalItem curItem, newItem;
+ int nIdleActions;
+ uint32 curTime;
+ uint32 dwSleepTime;
+ uint32 dwId;
+ int ord;
+ bool delayExpired;
+ bool expired;
+
+ MYACTION *myActions;
+ MYTHREAD *myThreads;
+
+ ~CoroContextTag() {
+ // Free data blocks
+ if (myThreads)
+ globalDestroy(myThreads);
+ if (myActions)
+ globalDestroy(myActions);
+ }
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 id = *((const uint32 *)param);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Initialize data pointers
+ _ctx->myActions = NULL;
+ _ctx->myThreads = NULL;
+
+ // To begin with, we need to request the item list from the location
+ _ctx->il = mpalQueryItemList(GLOBALS._nPollingLocations[id]);
+
+ // Count the items
+ for (_ctx->numitems = 0; _ctx->il[_ctx->numitems] != 0; _ctx->numitems++)
+ ;
+
+ // We look for items without idle actions, and eliminate them from the list
+ lockItems();
+ _ctx->nIdleActions = 0;
+ _ctx->nRealItems = 0;
+ for (_ctx->i = 0; _ctx->i < _ctx->numitems; _ctx->i++) {
+ _ctx->ord = itemGetOrderFromNum(_ctx->il[_ctx->i]);
+
+ if (_ctx->ord == -1)
+ continue;
+
+ _ctx->curItem = GLOBALS._lpmiItems + _ctx->ord;
+
+ _ctx->k = 0;
+ for (_ctx->j = 0; _ctx->j < _ctx->curItem->_nActions; _ctx->j++) {
+ if (_ctx->curItem->_action[_ctx->j]._num == 0xFF)
+ _ctx->k++;
+ }
+
+ _ctx->nIdleActions += _ctx->k;
+
+ if (_ctx->k == 0)
+ // We can remove this item from the list
+ _ctx->il[_ctx->i] = (uint32)NULL;
+ else
+ _ctx->nRealItems++;
+ }
+ unlockItems();
+
+ // If there is nothing left, we can exit
+ if (_ctx->nRealItems == 0) {
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+ _ctx->myThreads = (MYTHREAD *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _ctx->nRealItems * sizeof(MYTHREAD));
+ if (_ctx->myThreads == NULL) {
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+
+ // We have established that there is at least one item that contains idle actions.
+ // Now we created the mirrored copies of the idle actions.
+ _ctx->myActions = (MYACTION *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _ctx->nIdleActions * sizeof(MYACTION));
+ if (_ctx->myActions == NULL) {
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+ lockItems();
+ _ctx->k = 0;
+
+ for (_ctx->i = 0; _ctx->i < _ctx->numitems; _ctx->i++) {
+ if (_ctx->il[_ctx->i] == 0)
+ continue;
+
+ _ctx->curItem = GLOBALS._lpmiItems + itemGetOrderFromNum(_ctx->il[_ctx->i]);
+
+ for (_ctx->j = 0; _ctx->j < _ctx->curItem->_nActions; _ctx->j++) {
+ if (_ctx->curItem->_action[_ctx->j]._num == 0xFF) {
+ _ctx->myActions[_ctx->k]._nItem = _ctx->il[_ctx->i];
+ _ctx->myActions[_ctx->k]._nAction = _ctx->j;
+
+ _ctx->myActions[_ctx->k]._wTime = _ctx->curItem->_action[_ctx->j]._wTime;
+ _ctx->myActions[_ctx->k]._perc = _ctx->curItem->_action[_ctx->j]._perc;
+ _ctx->myActions[_ctx->k]._when = _ctx->curItem->_action[_ctx->j]._when;
+ _ctx->myActions[_ctx->k]._nCmds = _ctx->curItem->_action[_ctx->j]._nCmds;
+ memcpy(_ctx->myActions[_ctx->k]._cmdNum, _ctx->curItem->_action[_ctx->j]._cmdNum,
+ MAX_COMMANDS_PER_ACTION * sizeof(uint16));
+
+ _ctx->myActions[_ctx->k]._dwLastTime = g_vm->getTime();
+ _ctx->k++;
+ }
+ }
+ }
+
+ unlockItems();
+
+ // We don't need the item list anymore
+ globalDestroy(_ctx->il);
+
+
+ // Here's the main loop
+ while (1) {
+ // Searching for idle actions requiring time to execute
+ _ctx->curTime = g_vm->getTime();
+ _ctx->dwSleepTime = (uint32)-1L;
+
+ for (_ctx->k = 0;_ctx->k<_ctx->nIdleActions;_ctx->k++) {
+ if (_ctx->curTime >= _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime) {
+ _ctx->dwSleepTime = 0;
+ break;
+ } else
+ _ctx->dwSleepTime = MIN(_ctx->dwSleepTime, _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime - _ctx->curTime);
+ }
+
+ // We fall alseep, but always checking that the event is set when prompted for closure
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, GLOBALS._hEndPollingLocations[id], _ctx->dwSleepTime, &_ctx->expired);
+
+ //if (_ctx->k == WAIT_OBJECT_0)
+ if (!_ctx->expired)
+ break;
+
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem != 0) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _ctx->myThreads[_ctx->i]._hThread, 0, &_ctx->delayExpired);
+
+ // if result == WAIT_OBJECT_0)
+ if (!_ctx->delayExpired)
+ _ctx->myThreads[_ctx->i]._nItem = 0;
+ }
+ }
+
+ _ctx->curTime = g_vm->getTime();
+
+ // Loop through all the necessary idle actions
+ for (_ctx->k = 0; _ctx->k < _ctx->nIdleActions; _ctx->k++) {
+ if (_ctx->curTime >= _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime) {
+ _ctx->myActions[_ctx->k]._dwLastTime += _ctx->myActions[_ctx->k]._wTime;
+
+ // It's time to check to see if fortune is on the side of the idle action
+ byte randomVal = (byte)g_vm->_randomSource.getRandomNumber(99);
+ if (randomVal < _ctx->myActions[_ctx->k]._perc) {
+ // Check if there is an action running on the item
+ if ((GLOBALS._bExecutingAction) && (GLOBALS._nExecutingAction == _ctx->myActions[_ctx->k]._nItem))
+ continue;
+
+ // Check to see if there already another idle funning running on the item
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem == _ctx->myActions[_ctx->k]._nItem)
+ break;
+ }
+
+ if (_ctx->i < _ctx->nRealItems)
+ continue;
+
+ // Ok, we are the only ones :)
+ lockItems();
+ _ctx->curItem = GLOBALS._lpmiItems + itemGetOrderFromNum(_ctx->myActions[_ctx->k]._nItem);
+
+ // Check if there is a WhenExecute expression
+ _ctx->j=_ctx->myActions[_ctx->k]._nAction;
+ if (_ctx->curItem->_action[_ctx->j]._when != NULL) {
+ if (!evaluateExpression(_ctx->curItem->_action[_ctx->j]._when)) {
+ unlockItems();
+ continue;
+ }
+ }
+
+ // Ok, we can perform the action. For convenience, we do it in a new process
+ _ctx->newItem = (LpMpalItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalItem));
+ if (_ctx->newItem == false) {
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->myActions);
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ memcpy(_ctx->newItem,_ctx->curItem, sizeof(MpalItem));
+ unlockItems();
+
+ // We copy the action in #0
+ //_ctx->newItem->Action[0].nCmds = _ctx->curItem->Action[_ctx->j].nCmds;
+ //memcpy(_ctx->newItem->Action[0].CmdNum,_ctx->curItem->Action[_ctx->j].CmdNum,_ctx->newItem->Action[0].nCmds*sizeof(_ctx->newItem->Action[0].CmdNum[0]));
+ _ctx->newItem->_dwRes = _ctx->j;
+
+ // We will create an action, and will provide the necessary details
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem == 0)
+ break;
+ }
+
+ _ctx->myThreads[_ctx->i]._nItem = _ctx->myActions[_ctx->k]._nItem;
+
+ // Create the process
+ if ((_ctx->myThreads[_ctx->i]._hThread = CoroScheduler.createProcess(ActionThread, &_ctx->newItem, sizeof(LpMpalItem))) == CORO_INVALID_PID_VALUE) {
+ //if ((_ctx->myThreads[_ctx->i]._hThread = (void*)_beginthread(ActionThread, 10240, (void *)_ctx->newItem)) == (void*)-1)
+ globalDestroy(_ctx->newItem);
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->myActions);
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // Skip all idle actions of the same item
+ }
+ }
+ }
+ }
+
+
+ // Set idle skip on
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[200], 0, 0, 0, 0);
+
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem != 0) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _ctx->myThreads[_ctx->i]._hThread, 5000, &_ctx->delayExpired);
+
+ //if (result != WAIT_OBJECT_0)
+ //if (_ctx->delayExpired)
+ // TerminateThread(_ctx->MyThreads[_ctx->i].hThread, 0);
+
+ CoroScheduler.killMatchingProcess(_ctx->myThreads[_ctx->i]._hThread);
+ }
+ }
+
+ // Set idle skip off
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[201], 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Wait for the end of the dialog execution thread, and then restore global
+ * variables indicating that the dialogue has finished.
+ *
+ * @param param Pointer to a handle to the dialog
+ * @remarks This additional process is used, instead of clearing variables
+ * within the same dialog thread, because due to the recursive nature of a dialog,
+ * it would be difficult to know within it when the dialog is actually ending.
+ */
+void ShutUpDialogThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 pid = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE);
+
+ GLOBALS._bExecutingDialog = false;
+ GLOBALS._nExecutingDialog = 0;
+ GLOBALS._nExecutingChoice = 0;
+
+ CoroScheduler.setEvent(GLOBALS._hAskChoice);
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+void doChoice(CORO_PARAM, uint32 nChoice);
+
+
+/**
+ * Executes a group of the current dialog. Can 'be the Starting point of a process.
+ * @parm nGroup Number of the group to perform
+ */
+void GroupThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ LpMpalDialog dialog;
+ int i, j, k;
+ int type;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 nGroup = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Lock the _ctx->dialog
+ lockDialogs();
+
+ // Find the pointer to the current _ctx->dialog
+ _ctx->dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Search inside the group requesting the _ctx->dialog
+ for (_ctx->i = 0; _ctx->dialog->_group[_ctx->i]._num != 0; _ctx->i++) {
+ if (_ctx->dialog->_group[_ctx->i]._num == nGroup) {
+ // Cycle through executing the commands of the group
+ for (_ctx->j = 0; _ctx->j < _ctx->dialog->_group[_ctx->i]._nCmds; _ctx->j++) {
+ _ctx->k = _ctx->dialog->_group[_ctx->i]._cmdNum[_ctx->j];
+
+ _ctx->type = _ctx->dialog->_command[_ctx->k]._type;
+ if (_ctx->type == 1) {
+ // Call custom function
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->dialog->_command[_ctx->k]._nCf],
+ _ctx->dialog->_command[_ctx->k]._arg1,
+ _ctx->dialog->_command[_ctx->k]._arg2,
+ _ctx->dialog->_command[_ctx->k]._arg3,
+ _ctx->dialog->_command[_ctx->k]._arg4
+ );
+
+ } else if (_ctx->type == 2) {
+ // Set a variable
+ lockVar();
+ varSetValue(_ctx->dialog->_command[_ctx->k]._lpszVarName, evaluateExpression(_ctx->dialog->_command[_ctx->k]._expr));
+ unlockVar();
+
+ } else if (_ctx->type == 3) {
+ // DoChoice: call the chosen function
+ CORO_INVOKE_1(doChoice, (uint32)_ctx->dialog->_command[_ctx->k]._nChoice);
+
+ } else {
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // WORKAROUND: Wait for events to pulse.
+ CORO_SLEEP(1);
+ }
+
+ // The gruop is finished, so we can return to the calling function.
+ // If the group was the first called, then the process will automatically
+ // end. Otherwise it returns to the caller method
+
+ return;
+ }
+ }
+
+ // If we are here, it means that we have not found the requested group
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Make a choice in the current dialog.
+ *
+ * @param nChoice Number of choice to perform
+ */
+void doChoice(CORO_PARAM, uint32 nChoice) {
+ CORO_BEGIN_CONTEXT;
+ LpMpalDialog dialog;
+ int i, j, k;
+ uint32 nGroup;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Lock the dialogs
+ lockDialogs();
+
+ // Get a pointer to the current dialog
+ _ctx->dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Search the choice between those required in the dialog
+ for (_ctx->i = 0; _ctx->dialog->_choice[_ctx->i]._nChoice != 0; _ctx->i++) {
+ if (_ctx->dialog->_choice[_ctx->i]._nChoice == nChoice)
+ break;
+ }
+
+ // If nothing has been found, exit with an error
+ if (_ctx->dialog->_choice[_ctx->i]._nChoice == 0) {
+ // If we're here, we did not find the required choice
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // We've found the requested choice. Remember what in global variables
+ GLOBALS._nExecutingChoice = _ctx->i;
+
+ while (1) {
+ GLOBALS._nExecutingChoice = _ctx->i;
+
+ _ctx->k = 0;
+ // Calculate the expression of each selection, to see if they're active or inactive
+ for (_ctx->j = 0; _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._dwData != 0; _ctx->j++) {
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._when == NULL) {
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 1;
+ _ctx->k++;
+ } else if (evaluateExpression(_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._when)) {
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 1;
+ _ctx->k++;
+ } else
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 0;
+ }
+
+ // If there are no choices activated, then the dialog is finished.
+ if (_ctx->k == 0) {
+ unlockDialogs();
+ break;
+ }
+
+ // There are choices available to the user, so wait for them to make one
+ CoroScheduler.resetEvent(GLOBALS._hDoneChoice);
+ CoroScheduler.setEvent(GLOBALS._hAskChoice);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._hDoneChoice, CORO_INFINITE);
+
+ // Now that the choice has been made, we can run the groups associated with the choice tbontbtitq
+ _ctx->j = GLOBALS._nSelectedChoice;
+ for (_ctx->k = 0; _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._wPlayGroup[_ctx->k] != 0; _ctx->k++) {
+ _ctx->nGroup = _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._wPlayGroup[_ctx->k];
+ CORO_INVOKE_1(GroupThread, &_ctx->nGroup);
+ }
+
+ // Control attribute
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._attr & (1 << 0)) {
+ // Bit 0 set: the end of the choice
+ unlockDialogs();
+ break;
+ }
+
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._attr & (1 << 1)) {
+ // Bit 1 set: the end of the dialog
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // End of choic ewithout attributes. We must do it again
+ }
+
+ // If we're here, we found an end choice. Return to the caller group
+ return;
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Perform an action on a certain item.
+ *
+ * @param nAction Action number
+ * @param ordItem Index of the item in the items list
+ * @param dwParam Any parameter for the action.
+ * @returns Id of the process that was launched to perform the action, or
+ * CORO_INVALID_PID_VALUE if the action was not defined, or the item was inactive.
+ * @remarks You can get the index of an item from its number by using
+ * the itemGetOrderFromNum() function. The items list must first be locked
+ * by calling LockItem().
+ */
+static uint32 doAction(uint32 nAction, uint32 ordItem, uint32 dwParam) {
+ LpMpalItem item = GLOBALS._lpmiItems;
+ LpMpalItem newitem;
+
+ item+=ordItem;
+ Common::String buf = Common::String::format("Status.%u", item->_nObj);
+ if (varGetValue(buf.c_str()) <= 0)
+ return CORO_INVALID_PID_VALUE;
+
+ for (int i = 0; i < item->_nActions; i++) {
+ if (item->_action[i]._num != nAction)
+ continue;
+
+ if (item->_action[i]._wParm != dwParam)
+ continue;
+
+ if (item->_action[i]._when != NULL) {
+ if (!evaluateExpression(item->_action[i]._when))
+ continue;
+ }
+
+ // Now we find the right action to be performed
+ // Duplicate the item and copy the current action in #i into #0
+ newitem = (LpMpalItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalItem));
+ if (newitem == NULL)
+ return CORO_INVALID_PID_VALUE;
+
+ // In the new version number of the action in writing dwRes
+ Common::copy((byte *)item, (byte *)item + sizeof(MpalItem), (byte *)newitem);
+
+ //newitem->_action[0]._nCmds=item->_action[i]._nCmds;
+ //memcpy(newitem->_action[0]._cmdNum, item->_action[i]._cmdNum, newitem->Action[0].nCmds * sizeof(newitem->_action[0]._cmdNum[0]));
+
+ newitem->_dwRes = i;
+
+ // And finally we can laucnh the process that will execute the action,
+ // and a second process to free up the memory when the action is finished.
+
+ // !!! New process management
+ uint32 h;
+ if ((h = CoroScheduler.createProcess(ActionThread, &newitem, sizeof(LpMpalItem))) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ if (CoroScheduler.createProcess(ShutUpActionThread, &h, sizeof(uint32)) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ GLOBALS._nExecutingAction = item->_nObj;
+ GLOBALS._bExecutingAction = true;
+
+ return h;
+ }
+
+ return CORO_INVALID_PID_VALUE;
+}
+
+/**
+ * Shows a dialog in a separate process.
+ *
+ * @param nDlgOrd The index of the dialog in the dialog list
+ * @param nGroup Number of the group to perform
+ * @returns The process Id of the process running the dialog
+ * or CORO_INVALID_PID_VALUE on error
+ * @remarks The dialogue runs in a thread created on purpose,
+ * so that must inform through an event and when 'necessary to you make a choice.
+ * The data on the choices may be obtained through various queries.
+ */
+static uint32 doDialog(uint32 nDlgOrd, uint32 nGroup) {
+ // Store the running dialog in a global variable
+ GLOBALS._nExecutingDialog = nDlgOrd;
+
+ // Enables the flag to indicate that there is' a running dialogue
+ GLOBALS._bExecutingDialog = true;
+
+ CoroScheduler.resetEvent(GLOBALS._hAskChoice);
+ CoroScheduler.resetEvent(GLOBALS._hDoneChoice);
+
+ // Create a thread that performs the dialogue group
+
+ // Create the process
+ uint32 h;
+ if ((h = CoroScheduler.createProcess(GroupThread, &nGroup, sizeof(uint32))) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ // Create a thread that waits until the end of the dialog process, and will restore the global variables
+ if (CoroScheduler.createProcess(ShutUpDialogThread, &h, sizeof(uint32)) == CORO_INVALID_PID_VALUE) {
+ // Something went wrong, so kill the previously started dialog process
+ CoroScheduler.killMatchingProcess(h);
+ return CORO_INVALID_PID_VALUE;
+ }
+
+ return h;
+}
+
+
+/**
+ * Takes note of the selection chosen by the user, and warns the process that was running
+ * the box that it can continue.
+ *
+ * @param nChoice Number of choice that was in progress
+ * @param dwData Since combined with select selection
+ * @returns True if everything is OK, false on failure
+ */
+bool doSelection(uint32 i, uint32 dwData) {
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+ int j;
+
+ for (j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._dwData == dwData && dialog->_choice[i]._select[j]._curActive != 0)
+ break;
+ }
+
+ if (dialog->_choice[i]._select[j]._dwData == 0)
+ return false;
+
+ GLOBALS._nSelectedChoice = j;
+ CoroScheduler.setEvent(GLOBALS._hDoneChoice);
+ return true;
+}
+
+
+/**
+ * @defgroup Exported functions
+ */
+//@{
+
+/**
+ * Initializes the MPAL library and opens the .MPC file, which will be used for all queries.
+ *
+ * @param lpszMpcFileName Name of the MPC file
+ * @param lpszMprFileName Name of the MPR file
+ * @param lplpcfArray Array of pointers to custom functions.
+ * @returns True if everything is OK, false on failure
+ */
+bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName,
+ LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings) {
+ byte buf[5];
+ byte *cmpbuf;
+
+ // Save the array of custom functions
+ GLOBALS._lplpFunctions = lplpcfArray;
+ GLOBALS._lplpFunctionStrings = lpcfStrings;
+
+ // OPen the MPC file for reading
+ Common::File hMpc;
+ if (!hMpc.open(lpszMpcFileName))
+ return false;
+
+ // Read and check the header
+ uint32 nBytesRead = hMpc.read(buf, 5);
+ if (nBytesRead != 5)
+ return false;
+
+ if (buf[0] != 'M' || buf[1] != 'P' || buf[2] != 'C' || buf[3] != 0x20)
+ return false;
+
+ bool bCompress = buf[4];
+
+ // Reads the size of the uncompressed file, and allocate memory
+ uint32 dwSizeDecomp = hMpc.readUint32LE();
+ if (hMpc.err())
+ return false;
+
+ byte *lpMpcImage = (byte *)globalAlloc(GMEM_FIXED, dwSizeDecomp + 16);
+ if (lpMpcImage == NULL)
+ return false;
+
+ if (bCompress) {
+ // Get the compressed size and read the data in
+ uint32 dwSizeComp = hMpc.readUint32LE();
+ if (hMpc.err())
+ return false;
+
+ cmpbuf = (byte *)globalAlloc(GMEM_FIXED, dwSizeComp);
+ if (cmpbuf == NULL)
+ return false;
+
+ nBytesRead = hMpc.read(cmpbuf, dwSizeComp);
+ if (nBytesRead != dwSizeComp)
+ return false;
+
+ // Decompress the data
+ lzo1x_decompress(cmpbuf, dwSizeComp, lpMpcImage, &nBytesRead);
+ if (nBytesRead != dwSizeDecomp)
+ return false;
+
+ globalDestroy(cmpbuf);
+ } else {
+ // If the file is not compressed, we directly read in the data
+ nBytesRead = hMpc.read(lpMpcImage, dwSizeDecomp);
+ if (nBytesRead != dwSizeDecomp)
+ return false;
+ }
+
+ // Close the file
+ hMpc.close();
+
+ // Process the data
+ if (parseMpc(lpMpcImage) == false)
+ return false;
+
+ globalDestroy(lpMpcImage);
+
+ // Open the MPR file
+ if (!GLOBALS._hMpr.open(lpszMprFileName))
+ return false;
+
+ // Seek to the end of the file to read overall information
+ GLOBALS._hMpr.seek(-12, SEEK_END);
+
+ uint32 dwSizeComp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ GLOBALS._nResources = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ nBytesRead = GLOBALS._hMpr.read(buf, 4);
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ if (buf[0] !='E' || buf[1] != 'N' || buf[2] != 'D' || buf[3] != '0')
+ return false;
+
+ // Move to the start of the resources header
+ GLOBALS._hMpr.seek(-(12 + (int)dwSizeComp), SEEK_END);
+
+ GLOBALS._lpResources = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, GLOBALS._nResources * 8);
+ if (GLOBALS._lpResources == NULL)
+ return false;
+
+ cmpbuf = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, dwSizeComp);
+ if (cmpbuf == NULL)
+ return false;
+
+ nBytesRead = GLOBALS._hMpr.read(cmpbuf, dwSizeComp);
+ if (nBytesRead != dwSizeComp)
+ return false;
+
+ lzo1x_decompress((const byte *)cmpbuf, dwSizeComp, (byte *)GLOBALS._lpResources, (uint32 *)&nBytesRead);
+ if (nBytesRead != (uint32)GLOBALS._nResources * 8)
+ return false;
+
+ globalDestroy(cmpbuf);
+
+ // Reset back to the start of the file, leaving it open
+ GLOBALS._hMpr.seek(0, SEEK_SET);
+
+ // There is no action or dialog running by default
+ GLOBALS._bExecutingAction = false;
+ GLOBALS._bExecutingDialog = false;
+
+ // There's no polling location
+ Common::fill(GLOBALS._nPollingLocations, GLOBALS._nPollingLocations + MAXPOLLINGLOCATIONS, 0);
+
+ // Create the event that will be used to co-ordinate making choices and choices finishing
+ GLOBALS._hAskChoice = CoroScheduler.createEvent(true, false);
+ GLOBALS._hDoneChoice = CoroScheduler.createEvent(true, false);
+
+ return true;
+}
+
+/**
+ * Frees resources allocated by the MPAL subsystem
+ */
+void mpalFree() {
+ // Free the resource list
+ globalDestroy(GLOBALS._lpResources);
+}
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns numeric results.
+ */
+uint32 mpalQueryDWORD(uint16 wQueryType, ...) {
+ Common::String buf;
+ uint32 dwRet = 0;
+ char *n;
+
+ va_list v;
+ va_start(v, wQueryType);
+
+ GLOBALS._mpalError = OK;
+
+ if (wQueryType == MPQ_VERSION) {
+
+ /*
+ * uint32 mpalQuery(MPQ_VERSION);
+ */
+ dwRet = HEX_VERSION;
+
+ } else if (wQueryType == MPQ_GLOBAL_VAR) {
+ /*
+ * uint32 mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName);
+ */
+ lockVar();
+ dwRet = (uint32)varGetValue(GETARG(char *));
+ unlockVar();
+
+ } else if (wQueryType == MPQ_MESSAGE) {
+ /*
+ * char * mpalQuery(MPQ_MESSAGE, uint32 nMsg);
+ */
+ error("mpalQuery(MPQ_MESSAGE, uint32 nMsg) used incorrect method variant");
+
+
+ } else if (wQueryType == MPQ_ITEM_PATTERN) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem);
+ */
+ lockVar();
+ buf = Common::String::format("Pattern.%u", GETARG(uint32));
+ dwRet = (uint32)varGetValue(buf.c_str());
+ unlockVar();
+
+ } else if (wQueryType == MPQ_LOCATION_SIZE) {
+ /*
+ * uint32 mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord);
+ */
+ lockLocations();
+ int x = locGetOrderFromNum(GETARG(uint32));
+ int y = GETARG(uint32);
+ if (x != -1) {
+ if (y == MPQ_X)
+ dwRet = GLOBALS._lpmlLocations[x]._dwXlen;
+ else if (y == MPQ_Y)
+ dwRet = GLOBALS._lpmlLocations[x]._dwYlen;
+ else
+ GLOBALS._mpalError = 1;
+ } else
+ GLOBALS._mpalError = 1;
+
+ unlockLocations();
+
+ } else if (wQueryType == MPQ_LOCATION_IMAGE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc);
+ */
+ error("mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc) used incorrect variant");
+
+ } else if (wQueryType == MPQ_RESOURCE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_RESOURCE, uint32 dwRes);
+ */
+ error("mpalQuery(MPQ_RESOURCE, uint32 dwRes) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_LIST) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_LIST, uint32 nLoc);
+ */
+ error("mpalQuery(MPQ_ITEM_LIST, uint32 nLoc) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_DATA) {
+ /*
+ * LpItem mpalQuery(MPQ_ITEM_DATA, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_DATA, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_IS_ACTIVE) {
+ /*
+ * bool mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ dwRet = (uint32)false;
+ else
+ dwRet = (uint32)true;
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_ITEM_NAME) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_NAME, uint32 nItem, char * lpszName);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ n = GETARG(char *);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ n[0]='\0';
+ else {
+ lockItems();
+ int y = itemGetOrderFromNum(x);
+ memcpy(n, (char *)(GLOBALS._lpmiItems + y)->_lpszDescribe, MAX_DESCRIBE_SIZE);
+ unlockItems();
+ }
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_DIALOG_PERIOD) {
+ /*
+ * char *mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod);
+ */
+ error("mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ error("mpalQuery(MPQ_DIALOG_WAITFORCHOICE) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTLIST) {
+ /*
+ * uint32 *mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice);
+ */
+ error("mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTION) {
+ /*
+ * bool mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData);
+ */
+ lockDialogs();
+ int x = GETARG(uint32);
+ int y = GETARG(uint32);
+ dwRet = (uint32)doSelection(x, y);
+
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DO_ACTION) {
+ /*
+ * int mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam);
+ */
+ lockItems();
+ lockVar();
+ int x = GETARG(uint32);
+ int z = GETARG(uint32);
+ int y = itemGetOrderFromNum(z);
+ if (y != -1) {
+ dwRet = doAction(x, y, GETARG(uint32));
+ } else {
+ dwRet = CORO_INVALID_PID_VALUE;
+ GLOBALS._mpalError = 1;
+ }
+
+ unlockVar();
+ unlockItems();
+
+ } else if (wQueryType == MPQ_DO_DIALOG) {
+ /*
+ * int mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup);
+ */
+ if (!GLOBALS._bExecutingDialog) {
+ lockDialogs();
+
+ int x = dialogGetOrderFromNum(GETARG(uint32));
+ int y = GETARG(uint32);
+ dwRet = doDialog(x, y);
+ unlockDialogs();
+ }
+ } else {
+ /*
+ * DEFAULT -> ERROR
+ */
+ GLOBALS._mpalError = 1;
+ }
+
+ va_end(v);
+ return dwRet;
+}
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns a pointer or handle.
+ */
+MpalHandle mpalQueryHANDLE(uint16 wQueryType, ...) {
+ char *n;
+ Common::String buf;
+ va_list v;
+ va_start(v, wQueryType);
+ void *hRet = NULL;
+
+ GLOBALS._mpalError = OK;
+
+ if (wQueryType == MPQ_VERSION) {
+ /*
+ * uint32 mpalQuery(MPQ_VERSION);
+ */
+ error("mpalQuery(MPQ_VERSION) used incorrect variant");
+
+ } else if (wQueryType == MPQ_GLOBAL_VAR) {
+ /*
+ * uint32 mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName);
+ */
+ error("mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName) used incorrect variant");
+
+ } else if (wQueryType == MPQ_MESSAGE) {
+ /*
+ * char * mpalQuery(MPQ_MESSAGE, uint32 nMsg);
+ */
+ LockMsg();
+ hRet = DuplicateMessage(msgGetOrderFromNum(GETARG(uint32)));
+ UnlockMsg();
+
+ } else if (wQueryType == MPQ_ITEM_PATTERN) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_LOCATION_SIZE) {
+ /*
+ * uint32 mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord);
+ */
+ error("mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord) used incorrect variant");
+
+ } else if (wQueryType == MPQ_LOCATION_IMAGE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc);
+ */
+ lockLocations();
+ int x = locGetOrderFromNum(GETARG(uint32));
+ hRet = resLoad(GLOBALS._lpmlLocations[x]._dwPicRes);
+ unlockLocations();
+
+ } else if (wQueryType == MPQ_RESOURCE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_RESOURCE, uint32 dwRes);
+ */
+ hRet = resLoad(GETARG(uint32));
+
+ } else if (wQueryType == MPQ_ITEM_LIST) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_LIST, uint32 nLoc);
+ */
+ lockVar();
+ hRet = GetItemList(GETARG(uint32));
+ lockVar();
+
+ } else if (wQueryType == MPQ_ITEM_DATA) {
+ /*
+ * LpItem mpalQuery(MPQ_ITEM_DATA, uint32 nItem);
+ */
+ lockItems();
+ hRet = getItemData(itemGetOrderFromNum(GETARG(uint32)));
+ unlockItems();
+
+ } else if (wQueryType == MPQ_ITEM_IS_ACTIVE) {
+ /*
+ * bool mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_NAME) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_NAME, uint32 nItem, char *lpszName);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ n = GETARG(char *);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ n[0] = '\0';
+ else {
+ lockItems();
+ int y = itemGetOrderFromNum(x);
+ memcpy(n, (char *)(GLOBALS._lpmiItems + y)->_lpszDescribe, MAX_DESCRIBE_SIZE);
+ unlockItems();
+ }
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_DIALOG_PERIOD) {
+ /*
+ * char * mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod);
+ */
+ lockDialogs();
+ int y = GETARG(uint32);
+ hRet = duplicateDialogPeriod(y);
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ error("mpalQuery(MPQ_DIALOG_WAITFORCHOICE) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTLIST) {
+ /*
+ * uint32 *mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice);
+ */
+ lockDialogs();
+ hRet = getSelectList(GETARG(uint32));
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTION) {
+ /*
+ * bool mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData);
+ */
+ error("mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DO_ACTION) {
+ /*
+ * int mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam);
+ */
+ error("mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DO_DIALOG) {
+ /*
+ * int mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup);
+ */
+ error("mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup) used incorrect variant");
+ } else {
+ /*
+ * DEFAULT -> ERROR
+ */
+ GLOBALS._mpalError = 1;
+ }
+
+ va_end(v);
+ return hRet;
+}
+
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that needs to run within a co-routine context.
+ */
+void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, ...) {
+ CORO_BEGIN_CONTEXT;
+ uint32 dwRet;
+ CORO_END_CONTEXT(_ctx);
+
+ va_list v;
+ va_start(v, dwRet);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._hAskChoice, CORO_INFINITE);
+
+ // WORKAROUND: Introduce a single frame delay so that if there are multiple actions running,
+ // they all have time to be signalled before resetting the event. This fixes a problem where
+ // if you try to use the 'shrimp' on the parrot a second time after trying to first use it
+ // whilst the parrot was talking, the cursor wouldn't be re-enabled afterwards
+ CORO_SLEEP(1);
+
+ CoroScheduler.resetEvent(GLOBALS._hAskChoice);
+
+ if (GLOBALS._bExecutingDialog)
+ *dwRet = (uint32)GLOBALS._nExecutingChoice;
+ else
+ *dwRet = (uint32)((int)-1);
+ } else {
+ error("mpalQueryCORO called with unsupported query type");
+ }
+
+ CORO_END_CODE;
+
+ va_end(v);
+}
+
+/**
+ * Returns the current MPAL error code
+ *
+ * @returns Error code
+ */
+uint32 mpalGetError() {
+ return GLOBALS._mpalError;
+}
+
+/**
+ * Execute a script. The script runs on multitasking by a thread.
+ *
+ * @param nScript Script number to run
+ * @returns TRUE if the script 'was launched, FALSE on failure
+ */
+bool mpalExecuteScript(int nScript) {
+ LockScripts();
+ int n = scriptGetOrderFromNum(nScript);
+ LpMpalScript s = (LpMpalScript)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalScript));
+ if (s == NULL)
+ return false;
+
+ memcpy(s, GLOBALS._lpmsScripts + n, sizeof(MpalScript));
+ unlockScripts();
+
+ // !!! New process management
+ if (CoroScheduler.createProcess(ScriptThread, &s, sizeof(LpMpalScript)) == CORO_INVALID_PID_VALUE)
+ return false;
+
+ return true;
+}
+
+/**
+ * Install a custom routine That will be called by MPAL every time the pattern
+ * of an item has been changed.
+ *
+ * @param lpiifCustom Custom function to install
+ */
+void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCus) {
+ GLOBALS._lpiifCustom = lpiifCus;
+}
+
+/**
+ * Process the idle actions of the items on one location.
+ *
+ * @param nLoc Number of the location whose items must be processed
+ * for idle actions.
+ * @returns TRUE if all OK, and FALSE if it exceeded the maximum limit.
+ * @remarks The maximum number of locations that can be polled
+ * simultaneously is defined defined by MAXPOLLINGFUNCIONS
+ */
+bool mpalStartIdlePoll(int nLoc) {
+ for (uint32 i = 0; i < MAXPOLLINGLOCATIONS; i++) {
+ if (GLOBALS._nPollingLocations[i] == (uint32)nLoc)
+ return false;
+ }
+
+ for (uint32 i = 0; i < MAXPOLLINGLOCATIONS; i++) {
+ if (GLOBALS._nPollingLocations[i] == 0) {
+ GLOBALS._nPollingLocations[i] = nLoc;
+
+ GLOBALS._hEndPollingLocations[i] = CoroScheduler.createEvent(true, false);
+// !!! New process management
+ if ((GLOBALS._pollingThreads[i] = CoroScheduler.createProcess(LocationPollThread, &i, sizeof(uint32))) == CORO_INVALID_PID_VALUE)
+// if ((GLOBALS.hEndPollingLocations[i] = (void*)_beginthread(LocationPollThread, 10240, (void *)i))= = (void*)-1)
+ return false;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Stop processing the idle actions of the items on one location.
+ *
+ * @param nLo Number of the location
+ * @returns TRUE if all OK, FALSE if the specified location was not
+ * in the process of polling
+ */
+void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ for (_ctx->i = 0; _ctx->i < MAXPOLLINGLOCATIONS; _ctx->i++) {
+ if (GLOBALS._nPollingLocations[_ctx->i] == (uint32)nLoc) {
+ CoroScheduler.setEvent(GLOBALS._hEndPollingLocations[_ctx->i]);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._pollingThreads[_ctx->i], CORO_INFINITE);
+
+ CoroScheduler.closeEvent(GLOBALS._hEndPollingLocations[_ctx->i]);
+ GLOBALS._nPollingLocations[_ctx->i] = 0;
+
+ if (result)
+ *result = true;
+ return;
+ }
+ }
+
+ if (result)
+ *result = false;
+
+ CORO_END_CODE;
+}
+
+/**
+ * Retrieve the length of a save state
+ *
+ * @returns Length in bytes
+ */
+int mpalGetSaveStateSize() {
+ return GLOBALS._nVars * sizeof(MpalVar) + 4;
+}
+
+/**
+ * Store the save state into a buffer. The buffer must be
+ * length at least the size specified with mpalGetSaveStateSize
+ *
+ * @param buf Buffer where to store the state
+ */
+void mpalSaveState(byte *buf) {
+ lockVar();
+ WRITE_LE_UINT32(buf, GLOBALS._nVars);
+ memcpy(buf + 4, (byte *)GLOBALS._lpmvVars, GLOBALS._nVars * sizeof(MpalVar));
+ unlockVar();
+}
+
+
+/**
+ * Load a save state from a buffer.
+ *
+ * @param buf Buffer where to store the state
+ * @returns Length of the state buffer in bytes
+ */
+int mpalLoadState(byte *buf) {
+ // We must destroy and recreate all the variables
+ globalFree(GLOBALS._hVars);
+
+ GLOBALS._nVars = READ_LE_UINT32(buf);
+
+ GLOBALS._hVars = globalAllocate(GMEM_ZEROINIT | GMEM_MOVEABLE, GLOBALS._nVars * sizeof(MpalVar));
+ lockVar();
+ memcpy((byte *)GLOBALS._lpmvVars, buf + 4, GLOBALS._nVars * sizeof(MpalVar));
+ unlockVar();
+
+ return GLOBALS._nVars * sizeof(MpalVar) + 4;
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/mpal.h b/engines/tony/mpal/mpal.h
new file mode 100644
index 0000000000..5e1b02b3fc
--- /dev/null
+++ b/engines/tony/mpal/mpal.h
@@ -0,0 +1,518 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+
+/****************************************************************************\
+* General Introduction
+\****************************************************************************/
+
+/*
+ * MPAL (MultiPurpose Adventure Language) is a high level language
+ * for the definition of adventure. Through the use of MPAL you can describe
+ * storyboard the adventure, and then use it with any user interface.
+ * In fact, unlike many other similar products, MPAL is not programmed through
+ * the whole adventure, but are defined only the locations, objects, as they may
+ * interact with each other, etc.. thus making MPAL useful for any type of adventure.
+ */
+
+/****************************************************************************\
+* Structure
+\****************************************************************************/
+
+/*
+ * MPAL consists of two main files: MPAL.DLL and MPAL.H
+ * The first is the DLL that contains the code to interface with MPAL
+ * adventures, the second is the header that defines the prototypes
+ * functions. MPAL is compiled for Win32, and it can therefore be used with
+ * any compiler that supports Win32 DLL (Watcom C++, Visual C++,
+ * Delphi, etc.), and therefore compatible with both Windows 95 and Windows NT.
+ *
+ * To use the DLL, and 'obviously need to create a library for symbols to export.
+ *
+ */
+
+
+/****************************************************************************\
+* Custom Functions
+\****************************************************************************/
+
+/*
+ * A custom function and a function specified by the program that uses the
+ * library, to perform the particular code. The custom functions are
+ * retrieved from the library as specified in the source MPAL, and in particular
+ * in defining the behavior of an item with some action.
+ *
+ * To use the custom functions, you need to prepare an array of
+ * pointers to functions (such as using the type casting LPCUSTOMFUNCTION,
+ * (defined below), and pass it as second parameter to mpalInit (). Note you
+ * must specify the size of the array, as elements of pointers and which do not
+ * contain the same: the library will call it only those functions specified in
+ * the source MPAL. It can be useful, for debugging reasons, do not bet
+ * the shares of arrays used to debugging function, to avoid unpleasant crash,
+ * if it has been made an error in source and / or some oversight in the code.
+ *
+ */
+
+#ifndef TONY_MPAL_H
+#define TONY_MPAL_H
+
+#include "common/scummsys.h"
+#include "common/coroutines.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Macro definitions and structures
+\****************************************************************************/
+
+// OK value for the error codes
+#define OK 0
+
+#define MAXFRAMES 400 // frame animation of an object
+#define MAXPATTERN 40 // pattern of animation of an object
+#define MAXPOLLINGLOCATIONS 64
+
+#define GETARG(type) va_arg(v, type)
+
+/**
+ * Macro for use with queries that may refer to X and Y co-ordinates
+ */
+enum QueryCoordinates {
+ MPQ_X,
+ MPQ_Y
+};
+
+/**
+ * Query can be used with mpalQuery methods. In practice corresponds all claims
+ * that can do at the library
+ */
+enum QueryTypes {
+ // General Query
+ MPQ_VERSION = 10,
+
+ MPQ_GLOBAL_VAR = 50,
+ MPQ_RESOURCE,
+ MPQ_MESSAGE,
+
+ // Query on leases
+ MPQ_LOCATION_IMAGE = 100,
+ MPQ_LOCATION_SIZE,
+
+ // Queries about items
+ MPQ_ITEM_LIST = 200,
+ MPQ_ITEM_DATA,
+ MPQ_ITEM_PATTERN,
+ MPQ_ITEM_NAME,
+ MPQ_ITEM_IS_ACTIVE,
+
+ // Query dialog
+ MPQ_DIALOG_PERIOD = 300,
+ MPQ_DIALOG_WAITFORCHOICE,
+ MPQ_DIALOG_SELECTLIST,
+ MPQ_DIALOG_SELECTION,
+
+ // Query execution
+ MPQ_DO_ACTION = 400,
+ MPQ_DO_DIALOG
+};
+
+/**
+ * Framework to manage the animation of an item
+ */
+typedef struct {
+ char *_frames[MAXFRAMES];
+ Common::Rect _frameslocations[MAXFRAMES];
+ Common::Rect _bbox[MAXFRAMES];
+ short _pattern[MAXPATTERN][MAXFRAMES];
+ short _speed;
+ char _numframe;
+ char _numpattern;
+ char _curframe;
+ char _curpattern;
+ short _destX, _destY;
+ signed char _destZ;
+ short _objectID;
+} Item;
+typedef Item *LpItem;
+
+
+/**
+ * Define a custom function, to use the language MPAL to perform various controls as a result of an action
+ */
+typedef void (*LPCUSTOMFUNCTION)(CORO_PARAM, uint32, uint32, uint32, uint32);
+typedef LPCUSTOMFUNCTION *LPLPCUSTOMFUNCTION;
+
+/**
+ *
+ * Define an IRQ of an item that is called when the pattern changes or the status of an item
+ */
+typedef void (*LPITEMIRQFUNCTION)(uint32, int, int);
+typedef LPITEMIRQFUNCTION* LPLPITEMIRQFUNCTION;
+
+/**
+ * @defgroup Macrofunctions query
+ *
+ * The following are defines used for simplifying calling the mpalQuery variants
+ */
+//@{
+
+/**
+ * Gets the current version of MPAL
+ *
+ * @returns Version number (0x1232 = 1.2.3b)
+ */
+#define mpalQueryVersion() \
+ (uint16)mpalQueryDWORD(MPQ_VERSION)
+
+/**
+ * Gets the numerical value of a global variable
+ *
+ * @param lpszVarName Variable name (ASCIIZ)
+ * @returns Global variable value
+ * @remarks This query was implemented for debugging. The program,
+ * if well designed, should not need to access variables from
+ * within the library.
+ */
+#define mpalQueryGlobalVar(lpszVarName) \
+ mpalQueryDWORD(MPQ_GLOBAL_VAR, (const char *)(lpszVarName))
+
+
+/**
+ * Provides access to a resource inside the .MPC file
+ *
+ * @param dwResId Resource Id
+ * @returns Handle to a memory area containing the resource, ready for use.
+ */
+#define mpalQueryResource(dwResId) \
+ mpalQueryHANDLE(MPQ_RESOURCE, (uint32)(dwResId))
+
+
+/**
+ * Returns a message.
+ *
+ * @param nMsg Message number
+ * @returns ASCIIZ message
+ * @remarks The returned pointer must be freed via the memory manager
+* after use. The message will be in ASCIIZ format.
+*/
+#define mpalQueryMessage(nMsg) \
+ (char *)mpalQueryHANDLE(MPQ_MESSAGE, (uint32)(nMsg))
+
+
+/**
+ * Provides a location image
+ * @return Returns a picture handle
+ */
+#define mpalQueryLocationImage(nLoc) \
+ mpalQueryHANDLE(MPQ_LOCATION_IMAGE, (uint32)(nLoc))
+
+
+/**
+ * Request the x or y size of a location in pixels
+ *
+ * @param nLoc Location number
+ * @param dwCoord MPQ_X or MPQ_Y coordinate to retrieve
+ * @returns Size
+ */
+#define mpalQueryLocationSize(nLoc, dwCoord) \
+ mpalQueryDWORD(MPQ_LOCATION_SIZE, (uint32)(nLoc), (uint32)(dwCoord))
+
+
+/**
+ * Provides the list of objects in a location.
+ *
+ * @param nLoc Location number
+ * @returns List of objects (accessible by Item [0], Item [1], etc.)
+ */
+// TODO: Determine if this is endian safe
+#define mpalQueryItemList(nLoc) \
+ (uint32 *)mpalQueryHANDLE(MPQ_ITEM_LIST, (uint32)(nLoc))
+
+
+/**
+ * Provides information on an item
+ *
+ * @param nItem Item number
+ * @returns Structure filled with requested information
+ */
+#define mpalQueryItemData(nItem) \
+ (LpItem)mpalQueryHANDLE(MPQ_ITEM_DATA, (uint32)(nItem))
+
+
+/**
+ * Provides the current pattern of an item
+ *
+ * @param nItem Item number
+ * @returns Number of animation patterns to be executed.
+ * @remarks By default, the pattern of 0 indicates that we should do nothing.
+ */
+#define mpalQueryItemPattern(nItem) \
+ mpalQueryDWORD(MPQ_ITEM_PATTERN, (uint32)(nItem))
+
+
+/**
+ * Returns true if an item is active
+ *
+ * @param nItem Item number
+ * @returns TRUE if the item is active, FALSE otherwise
+ */
+#define mpalQueryItemIsActive(nItem) \
+ (bool)mpalQueryDWORD(MPQ_ITEM_IS_ACTIVE, (uint32)(nItem))
+
+
+/**
+ * Returns the name of an item
+ *
+ * @param nItem Item number
+ * @param lpszName Pointer to a buffer of at least 33 bytes
+ * that will be filled with the name
+ * @remarks If the item is not active (ie. if its status or number
+ * is less than or equal to 0), the string will be empty.
+ */
+#define mpalQueryItemName(nItem, lpszName) \
+ mpalQueryHANDLE(MPQ_ITEM_NAME, (uint32)(nItem), (char *)(lpszName))
+
+
+/**
+ * Returns a sentence of dialog.
+ *
+ * @param nDialog Dialog number
+ * @param nPeriod Number of words
+ * @returns A pointer to the string of words, or NULL on failure.
+ * @remarks The string must be freed after use using the memory manager.
+ * Unlike normal messages, the sentences of dialogue are formed by a single
+ * string terminated with 0.
+ */
+#define mpalQueryDialogPeriod(nPeriod) \
+ (char *)mpalQueryHANDLE(MPQ_DIALOG_PERIOD, (uint32)(nPeriod))
+
+
+/**
+ * Wait until the moment in which the need is signaled to make a choice by the user.
+ * @returns Number of choice to be made, or -1 if the dialogue is finished.
+ */
+#define mpalQueryDialogWaitForChoice(dwRet) \
+ CORO_INVOKE_2(mpalQueryCORO, MPQ_DIALOG_WAITFORCHOICE, dwRet)
+
+/**
+ * Requires a list of various options for some choice within the current dialog.
+ *
+ * @param nChoice Choice number
+ * @returns A pointer to an array containing the data matched to each option.
+ * @remarks The figure 'a uint32 specified in the source to which MPAL
+ * You can assign meaning that the more' suits.
+ * The pointer msut be freed after use using the memory memory.
+ */
+#define mpalQueryDialogSelectList(nChoice) \
+ (uint32 *)mpalQueryHANDLE(MPQ_DIALOG_SELECTLIST, (uint32)(nChoice))
+
+/**
+ * Warns the library that the user has selected, in a certain choice of the current dialog,
+ * corresponding option at a certain given.
+ *
+ * @param nChoice Choice number of the choice that was in progress
+ * @param dwData Option that was selected by the user.
+ * @returns TRUE if all OK, FALSE on failure.
+ * @remarks After execution of this query, MPAL continue
+ * Groups according to the execution of the dialogue. And necessary so the game
+ * remains on hold again for another chosen by mpalQueryDialogWaitForChoice ().
+ */
+#define mpalQueryDialogSelection(nChoice, dwData) \
+ (bool)mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))
+
+#define mpalQueryDialogSelectionDWORD(nChoice, dwData) \
+ mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))
+
+/**
+ * Warns the library an action was performed on a Object.
+ * The library will call custom functions, if necessary.
+ *
+ * @param nAction Action number
+ * @param nItem Item number
+ * @param dwParam Action parameter
+ * @returns Handle to the thread that is performing the action, or CORO_INVALID_PID_VALUE
+ * if the action is not defined for the item, or the item is inactive.
+ * @remarks The parameter is used primarily to implement actions
+ * as "U.S." involving two objects together. The action will be executed only
+ * if the item is active, ie if its status is a positive number greater than 0.
+ */
+#define mpalQueryDoAction(nAction, nItem, dwParam) \
+ mpalQueryDWORD(MPQ_DO_ACTION, (uint32)(nAction), (uint32)(nItem), (uint32)(dwParam))
+
+/**
+ * Warns the library a dialogue was required.
+ *
+ * @param nDialog Dialog number
+ * @param nGroup Group number to use
+ * @returns Handle to the thread that is running the box, or
+ * CORO_INVALID_PID_VALUE if the dialogue does not exist.
+ */
+#define mpalQueryDoDialog(nDialog, nGroup) \
+ mpalQueryDWORD(MPQ_DO_DIALOG, (uint32)(nDialog), (uint32)(nGroup))
+
+/**
+ * @defgroup Functions exported to the main game
+ */
+//@{
+
+/**
+ * Initializes the MPAL library, and opens an .MPC file, which will be 'used for all queries
+ * @param lpszMpcFileName Name of the .MPC file, including extension
+ * @param lpszMprFileName Name of the .MPR file, including extension
+ * @param lplpcfArray Array of pointers to custom functions
+ * @returns TRUE if all OK, FALSE on failure
+ */
+bool mpalInit(const char *lpszFileName, const char *lpszMprFileName,
+ LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings);
+
+/**
+ * Frees resources allocated by the MPAL subsystem
+ */
+void mpalFree();
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns numeric results.
+ */
+uint32 mpalQueryDWORD(uint16 wQueryType, ...);
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns a pointer or handle.
+ */
+MpalHandle mpalQueryHANDLE(uint16 wQueryType, ...);
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that needs to run within a co-routine context.
+ */
+void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, ...);
+
+/**
+ * Execute a script. The script runs on multitasking by a thread.
+ *
+ * @param nScript Script number to run
+ * @returns TRUE if the script 'was launched, FALSE on failure
+ */
+bool mpalExecuteScript(int nScript);
+
+/**
+ * Returns the current MPAL error code
+ *
+ * @returns Error code
+ */
+uint32 mpalGetError();
+
+/**
+ * Install a custom routine That will be called by MPAL every time the pattern
+ * of an item has been changed.
+ *
+ * @param lpiifCustom Custom function to install
+ */
+void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCustom);
+
+/**
+ * Process the idle actions of the items on one location.
+ *
+ * @param nLoc Number of the location whose items must be processed
+ * for idle actions.
+ * @returns TRUE if all OK, and FALSE if it exceeded the maximum limit.
+ * @remarks The maximum number of locations that can be polled
+ * simultaneously is defined defined by MAXPOLLINGFUNCIONS
+ */
+bool mpalStartIdlePoll(int nLoc);
+
+/**
+ * Stop processing the idle actions of the items on one location.
+ *
+ * @param nLo Number of the location
+ * @returns TRUE if all OK, FALSE if the specified location was not
+ * in the process of polling
+ */
+void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result);
+
+
+/**
+ * Load a save state from a buffer.
+ *
+ * @param buf Buffer where to store the state
+ * @returns Length of the state buffer in bytes
+ */
+int mpalLoadState(byte *buf);
+
+/**
+ * Store the save state into a buffer. The buffer must be
+ * length at least the size specified with mpalGetSaveStateSize
+ *
+ * @param buf Buffer where to store the state
+ */
+void mpalSaveState(byte *buf);
+
+/**
+ * Retrieve the length of a save state
+ *
+ * @returns Length in bytes
+ */
+int mpalGetSaveStateSize();
+
+/**
+ * Locks the variables for access
+ */
+void lockVar();
+
+/**
+ * Unlocks variables after use
+ */
+void unlockVar();
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/mpaldll.h b/engines/tony/mpal/mpaldll.h
new file mode 100644
index 0000000000..8897096f51
--- /dev/null
+++ b/engines/tony/mpal/mpaldll.h
@@ -0,0 +1,251 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef __MPALDLL_H
+#define __MPALDLL_H
+
+#include "common/file.h"
+#include "tony/mpal/memory.h"
+#include "tony/mpal/loadmpc.h"
+#include "tony/mpal/expr.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Defines
+\****************************************************************************/
+
+#define HEX_VERSION 0x0170
+
+#define MAX_ACTIONS_PER_ITEM 40
+#define MAX_COMMANDS_PER_ITEM 128
+#define MAX_COMMANDS_PER_ACTION 128
+#define MAX_DESCRIBE_SIZE 64
+
+#define MAX_MOMENTS_PER_SCRIPT 256
+#define MAX_COMMANDS_PER_SCRIPT 256
+#define MAX_COMMANDS_PER_MOMENT 32
+
+#define MAX_GROUPS_PER_DIALOG 128
+#define MAX_COMMANDS_PER_DIALOG 480
+#define MAX_COMMANDS_PER_GROUP 64
+#define MAX_CHOICES_PER_DIALOG 64
+#define MAX_SELECTS_PER_CHOICE 64
+#define MAX_PLAYGROUPS_PER_SELECT 9
+#define MAX_PERIODS_PER_DIALOG 400
+
+#define NEED_LOCK_MSGS
+
+/****************************************************************************\
+* Structures
+\****************************************************************************/
+
+#include "common/pack-start.h"
+
+/**
+ * MPAL global variables
+ */
+struct MpalVar {
+ uint32 _dwVal; // Variable value
+ char _lpszVarName[33]; // Variable name
+} PACKED_STRUCT;
+typedef MpalVar *LpMpalVar;
+
+/**
+ * MPAL Messages
+ */
+struct MpalMsg {
+ MpalHandle _hText; // Handle to the message text
+ uint16 _wNum; // Message number
+} PACKED_STRUCT;
+typedef MpalMsg *LpMpalMsg;
+
+/**
+ * MPAL Locations
+ */
+struct MpalLocation {
+ uint32 _nObj; // Location number
+ uint32 _dwXlen, _dwYlen; // Dimensions
+ uint32 _dwPicRes; // Resource that contains the image
+} PACKED_STRUCT;
+typedef MpalLocation *LpMpalLocation;
+
+/**
+ * All the data for a command, ie. tags used by OnAction in the item, the time
+ * in the script, and in the group dialog.
+ */
+struct Command {
+ /*
+ * Types of commands that are recognized
+ *
+ * #1 -> Custom function call (ITEM, SCRIPT, DIALOG)
+ * #2 -> Variable assignment (ITEM, SCRIPT, DIALOG)
+ * #3 -> Making a choice (DIALOG)
+ *
+ */
+ byte _type; // Type of control
+
+ union {
+ int32 _nCf; // Custom function call [#1]
+ char *_lpszVarName; // Variable name [#2]
+ int32 _nChoice; // Number of choice you make [#3]
+ };
+
+ union {
+ int32 _arg1; // Argument for custom function [#1]
+ MpalHandle _expr; // Expression to assign to a variable [#2]
+ };
+
+ int32 _arg2, _arg3, _arg4; // Arguments for custom function [#1]
+} PACKED_STRUCT;
+
+
+/**
+ * MPAL dialog
+ */
+struct MpalDialog {
+ uint32 _nObj; // Dialog number
+
+ struct Command _command[MAX_COMMANDS_PER_DIALOG];
+
+ struct {
+ uint16 _num;
+ byte _nCmds;
+ uint16 _cmdNum[MAX_COMMANDS_PER_GROUP];
+
+ } _group[MAX_GROUPS_PER_DIALOG];
+
+ struct {
+ // The last choice has nChoice == 0
+ uint16 _nChoice;
+
+ // The select number (we're pretty stingy with RAM). The last select has dwData == 0
+ struct {
+ MpalHandle _when;
+ uint32 _dwData;
+ uint16 _wPlayGroup[MAX_PLAYGROUPS_PER_SELECT];
+
+ // Bit 0=endchoice Bit 1=enddialog
+ byte _attr;
+
+ // Modified at run-time: 0 if the select is currently disabled,
+ // and 1 if currently active
+ byte _curActive;
+ } _select[MAX_SELECTS_PER_CHOICE];
+
+ } _choice[MAX_CHOICES_PER_DIALOG];
+
+ uint16 _periodNums[MAX_PERIODS_PER_DIALOG];
+ MpalHandle _periods[MAX_PERIODS_PER_DIALOG];
+
+} PACKED_STRUCT;
+typedef MpalDialog *LpMpalDialog;
+
+/**
+ * MPAL Item
+ */
+struct ItemAction {
+ byte _num; // Action number
+ uint16 _wTime; // If idle, the time which must pass
+ byte _perc; // Percentage of the idle run
+ MpalHandle _when; // Expression to compute. If != 0, then
+ // action can be done
+ uint16 _wParm; // Parameter for action
+
+ byte _nCmds; // Number of commands to be executed
+ uint32 _cmdNum[MAX_COMMANDS_PER_ACTION]; // Commands to execute
+} PACKED_STRUCT;
+
+struct MpalItem {
+ uint32 _nObj; // Item number
+
+ byte _lpszDescribe[MAX_DESCRIBE_SIZE]; // Name
+ byte _nActions; // Number of managed actions
+ uint32 _dwRes; // Resource that contains frames and patterns
+
+ struct Command _command[MAX_COMMANDS_PER_ITEM];
+
+ // Pointer to array of structures containing various managed activities. In practice, of
+ // every action we know what commands to run, including those defined in structures above
+ struct ItemAction *_action;
+
+} PACKED_STRUCT;
+typedef MpalItem *LpMpalItem;
+
+/**
+ * MPAL Script
+ */
+struct MpalScript {
+ uint32 _nObj;
+ uint32 _nMoments;
+
+ struct Command _command[MAX_COMMANDS_PER_SCRIPT];
+
+ struct {
+ int32 _dwTime;
+ byte _nCmds;
+ uint32 _cmdNum[MAX_COMMANDS_PER_MOMENT];
+
+ } _moment[MAX_MOMENTS_PER_SCRIPT];
+
+} PACKED_STRUCT;
+typedef MpalScript *LpMpalScript;
+
+#include "common/pack-end.h"
+
+/****************************************************************************\
+* Function prototypes
+\****************************************************************************/
+
+/**
+ * Returns the current value of a global variable
+ *
+ * @param lpszVarName Name of the variable
+ * @returns Current value
+ * @remarks Before using this method, you must call LockVar() to
+ * lock the global variablves for use. Then afterwards, you will
+ * need to remember to call UnlockVar()
+ */
+extern int32 varGetValue(const char *lpszVarName);
+
+
+/**
+ * Sets the value of a MPAL global variable
+ * @param lpszVarName Name of the variable
+ * @param val Value to set
+ */
+extern void varSetValue(const char *lpszVarName, int32 val);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/mpalutils.cpp b/engines/tony/mpal/mpalutils.cpp
new file mode 100644
index 0000000000..0919aed5ac
--- /dev/null
+++ b/engines/tony/mpal/mpalutils.cpp
@@ -0,0 +1,115 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#include "tony/mpal/mpalutils.h"
+#include "tony/tony.h"
+#include "common/memstream.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* RMRes methods
+\****************************************************************************/
+
+/**
+ * Constructor
+ * @param resId MPAL resource to open
+ */
+RMRes::RMRes(uint32 resID) {
+ _h = g_vm->_resUpdate.queryResource(resID);
+ if (_h == NULL)
+ _h = mpalQueryResource(resID);
+ if (_h != NULL)
+ _buf = (byte *)globalLock(_h);
+}
+
+/**
+ * Destructor
+ */
+RMRes::~RMRes() {
+ if (_h != NULL) {
+ globalUnlock(_h);
+ globalFree(_h);
+ }
+}
+
+/**
+ * Returns a pointer to the resource
+ */
+const byte *RMRes::dataPointer() {
+ return _buf;
+}
+
+/**
+ * Returns a pointer to the resource
+ */
+RMRes::operator const byte *() {
+ return dataPointer();
+}
+
+/**
+ * Returns the size of the resource
+ */
+unsigned int RMRes::size() {
+ return globalSize(_h);
+}
+
+Common::SeekableReadStream *RMRes::getReadStream() {
+ return new Common::MemoryReadStream(_buf, size());
+}
+
+bool RMRes::isValid() {
+ return _h != NULL;
+}
+
+/****************************************************************************\
+* RMResRaw methods
+\****************************************************************************/
+
+RMResRaw::RMResRaw(uint32 resID) : RMRes(resID) {
+}
+
+RMResRaw::~RMResRaw() {
+}
+
+const byte *RMResRaw::dataPointer() {
+ return _buf + 8;
+}
+
+RMResRaw::operator const byte *() {
+ return dataPointer();
+}
+
+int RMResRaw::width() {
+ return READ_LE_UINT16(_buf + 4);
+}
+
+int RMResRaw::height() {
+ return READ_LE_UINT16(_buf + 6);
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/mpalutils.h b/engines/tony/mpal/mpalutils.h
new file mode 100644
index 0000000000..d92bb6f9a2
--- /dev/null
+++ b/engines/tony/mpal/mpalutils.h
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef TONY_MPAL_MPALUTILS
+#define TONY_MPAL_MPALUTILS
+
+#include "common/scummsys.h"
+#include "tony/mpal/memory.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Tony {
+
+namespace MPAL {
+
+class RMRes {
+protected:
+ MpalHandle _h;
+ byte *_buf;
+
+public:
+ RMRes(uint32 resID);
+ virtual ~RMRes();
+
+ // Attributes
+ unsigned int size();
+ const byte *dataPointer();
+ bool isValid();
+
+ // Casting for access to data
+ operator const byte*();
+
+ Common::SeekableReadStream *getReadStream();
+};
+
+class RMResRaw : public RMRes {
+public:
+ RMResRaw(uint32 resID);
+ virtual ~RMResRaw();
+
+ const byte *dataPointer();
+ operator const byte*();
+
+ int width();
+ int height();
+};
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
diff --git a/engines/tony/resid.h b/engines/tony/resid.h
new file mode 100644
index 0000000000..f4d2c9a4fa
--- /dev/null
+++ b/engines/tony/resid.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+// From 10500 onwards there are .OGG for inventory and scrap
+#ifndef TONY_RESID_H
+#define TONY_RESID_H
+
+
+#define RES_I_INTERFACE 10300
+#define RES_I_INTERPAL 10301
+#define RES_I_INTERPPAL 10302
+#define RES_I_INTERP1 10303
+#define RES_I_INTERP2 10304
+#define RES_I_INTERP3 10305
+#define RES_I_INTERP4 10306
+#define RES_I_INTERP5 10307
+
+#define RES_I_DLGTEXT 10350
+#define RES_I_DLGTEXTLINE 10351
+#define RES_I_DLGTEXTPAL 10352
+
+#define RES_I_MINIINTER 10360
+
+#define RES_P_PAL 10410
+#define RES_P_GO 10400
+#define RES_P_TAKE 10401
+#define RES_P_USE 10402
+#define RES_P_EXAM 10403
+#define RES_P_TALK 10404
+
+#define RES_P_PAP1 10420
+#define RES_P_PAP2 10421
+#define RES_P_PAP3 10422
+#define RES_P_PAP4 10423
+#define RES_P_FRMAP 10424
+
+#define RES_F_PAL 10700
+#define RES_F_PARL 10701
+#define RES_F_OBJ 10702
+#define RES_F_MACC 10703
+#define RES_F_CREDITS 10704
+#define RES_F_CPAL 10705
+
+#define RES_W_CIRCLE 10800
+
+#endif
diff --git a/engines/tony/sound.cpp b/engines/tony/sound.cpp
new file mode 100644
index 0000000000..2844e0d925
--- /dev/null
+++ b/engines/tony/sound.cpp
@@ -0,0 +1,688 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "audio/audiostream.h"
+#include "audio/decoders/adpcm.h"
+#include "audio/decoders/raw.h"
+#include "audio/decoders/wave.h"
+#include "common/textconsole.h"
+#include "tony/game.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* FPSOUND Methods
+\****************************************************************************/
+
+/**
+ * Default constructor. Initializes the attributes.
+ *
+ */
+FPSound::FPSound() {
+ _soundSupported = false;
+}
+
+/**
+ * Initializes the object, and prepare everything you need to create streams and sound effects.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+bool FPSound::init() {
+ _soundSupported = g_system->getMixer()->isReady();
+ return _soundSupported;
+}
+
+/**
+ * Destroy the object and free the memory
+ *
+ */
+
+FPSound::~FPSound() {
+}
+
+/**
+ * Allocates an object of type FPStream, and return its pointer
+ *
+ * @param streamPtr Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSound::createStream(FPStream **streamPtr) {
+ (*streamPtr) = new FPStream(_soundSupported);
+
+ return (*streamPtr != NULL);
+}
+
+/**
+ * Allocates an object of type FpSfx, and return its pointer
+ *
+ * @param soundPtr Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSound::createSfx(FPSfx **sfxPtr) {
+ (*sfxPtr) = new FPSfx(_soundSupported);
+
+ return (*sfxPtr != NULL);
+}
+
+/**
+ * Set the general volume
+ *
+ * @param volume Volume to set (0-63)
+ */
+
+void FPSound::setMasterVolume(int volume) {
+ if (!_soundSupported)
+ return;
+
+ g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, CLIP<int>(volume, 0, 63) * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Get the general volume
+ *
+ * @param volumePtr Variable that will contain the volume (0-63)
+ */
+
+void FPSound::getMasterVolume(int *volumePtr) {
+ if (!_soundSupported)
+ return;
+
+ *volumePtr = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 63 / Audio::Mixer::kMaxChannelVolume;
+}
+
+/**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateSfx()
+ *
+ */
+
+FPSfx::FPSfx(bool soundOn) {
+ _soundSupported = soundOn;
+ _fileLoaded = false;
+ _lastVolume = 63;
+ _hEndOfBuffer = CoroScheduler.createEvent(true, false);
+ _isVoice = false;
+ _loopStream = 0;
+ _rewindableStream = 0;
+ _paused = false;
+
+ g_vm->_activeSfx.push_back(this);
+}
+
+/**
+ * Default Destructor.
+ *
+ * @remarks It is also stops the sound effect that may be
+ * currently played, and free the memory it uses.
+ *
+ */
+
+FPSfx::~FPSfx() {
+ if (!_soundSupported)
+ return;
+
+ g_system->getMixer()->stopHandle(_handle);
+ g_vm->_activeSfx.remove(this);
+
+ if (_loopStream)
+ delete _loopStream; // _rewindableStream is deleted by deleting _loopStream
+ else
+ delete _rewindableStream;
+
+ // Free the buffer end event
+ CoroScheduler.closeEvent(_hEndOfBuffer);
+}
+
+/**
+ * Releases the memory used by the object.
+ *
+ * @remarks Must be called when the object is no longer used and
+ * **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+
+void FPSfx::release() {
+ delete this;
+}
+
+bool FPSfx::loadWave(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ _rewindableStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
+
+ if (!_rewindableStream)
+ return false;
+
+ _fileLoaded = true;
+ setVolume(_lastVolume);
+ return true;
+}
+
+bool FPSfx::loadVoiceFromVDB(Common::File &vdbFP) {
+ if (!_soundSupported)
+ return true;
+
+ uint32 size = vdbFP.readUint32LE();
+ uint32 rate = vdbFP.readUint32LE();
+ _isVoice = true;
+
+ _rewindableStream = Audio::makeADPCMStream(vdbFP.readStream(size), DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, 1);
+
+ _fileLoaded = true;
+ setVolume(62);
+ return true;
+}
+
+/**
+ * Opens a file and loads a sound effect.
+ *
+ * @param fileName Sfx filename
+ * @param codec CODEC used to uncompress the samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::loadFile(const char *fileName, uint32 codec) {
+ if (!_soundSupported)
+ return true;
+
+ Common::File file;
+ if (!file.open(fileName)) {
+ warning("FPSfx::LoadFile(): Cannot open sfx file!");
+ return false;
+ }
+
+ if (file.readUint32BE() != MKTAG('A', 'D', 'P', 0x10)) {
+ warning("FPSfx::LoadFile(): Invalid ADP header!");
+ return false;
+ }
+
+ uint32 rate = file.readUint32LE();
+ uint32 channels = file.readUint32LE();
+
+ Common::SeekableReadStream *buffer = file.readStream(file.size() - file.pos());
+
+ if (codec == FPCODEC_ADPCM) {
+ _rewindableStream = Audio::makeADPCMStream(buffer, DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, channels);
+ } else {
+ byte flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
+
+ if (channels == 2)
+ flags |= Audio::FLAG_STEREO;
+
+ _rewindableStream = Audio::makeRawStream(buffer, rate, flags, DisposeAfterUse::YES);
+ }
+
+ _fileLoaded = true;
+ return true;
+}
+
+/**
+ * Play the Sfx in memory.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::play() {
+ stop(); // sanity check
+
+ if (_fileLoaded) {
+ CoroScheduler.resetEvent(_hEndOfBuffer);
+
+ _rewindableStream->rewind();
+
+ Audio::AudioStream *stream = _rewindableStream;
+
+ if (_loop) {
+ if (!_loopStream)
+ _loopStream = Audio::makeLoopingAudioStream(_rewindableStream, 0);
+
+ stream = _loopStream;
+ }
+
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream, -1,
+ Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+
+ setVolume(_lastVolume);
+
+ if (_paused)
+ g_system->getMixer()->pauseHandle(_handle, true);
+ }
+
+ return true;
+}
+
+/**
+ * Stops a Sfx.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::stop() {
+ if (_fileLoaded) {
+ g_system->getMixer()->stopHandle(_handle);
+ _paused = false;
+ }
+
+ return true;
+}
+
+/**
+ * Enables or disables the Sfx loop.
+ *
+ * @param loop True to enable the loop, False to disable
+ *
+ * @remarks The loop must be activated BEFORE the sfx starts
+ * playing. Any changes made during the play will have
+ * no effect until the sfx is stopped then played again.
+ */
+
+void FPSfx::setLoop(bool loop) {
+ _loop = loop;
+}
+
+/**
+ * Pauses a Sfx.
+ *
+ */
+
+void FPSfx::setPause(bool pause) {
+ if (_fileLoaded) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle) && (pause ^ _paused))
+ g_system->getMixer()->pauseHandle(_handle, pause);
+
+ _paused = pause;
+ }
+}
+
+/**
+ * Change the volume of Sfx
+ *
+ * @param volume Volume to be set (0-63)
+ *
+ */
+
+void FPSfx::setVolume(int volume) {
+ if (volume > 63)
+ volume = 63;
+
+ if (volume < 0)
+ volume = 0;
+
+ _lastVolume = volume;
+
+ if (_isVoice) {
+ if (!GLOBALS._bCfgDubbing)
+ volume = 0;
+ else {
+ volume -= (10 - GLOBALS._nCfgDubbingVolume) * 2;
+ if (volume < 0)
+ volume = 0;
+ }
+ } else {
+ if (!GLOBALS._bCfgSFX)
+ volume = 0;
+ else {
+ volume -= (10 - GLOBALS._nCfgSFXVolume) * 2;
+ if (volume < 0)
+ volume = 0;
+ }
+ }
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->setChannelVolume(_handle, volume * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Gets the Sfx volume
+ *
+ * @param volumePtr Will contain the current Sfx volume
+ *
+ */
+
+void FPSfx::getVolume(int *volumePtr) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ *volumePtr = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume;
+ else
+ *volumePtr = 0;
+}
+
+/**
+ * Returns true if the underlying sound has ended
+ */
+
+bool FPSfx::endOfBuffer() const {
+ return !g_system->getMixer()->isSoundHandleActive(_handle) && (!_rewindableStream || _rewindableStream->endOfData());
+}
+
+/**
+ * Continually checks to see if active sounds have finished playing
+ * Sets the event signalling the sound has ended
+ */
+void FPSfx::soundCheckProcess(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ Common::List<FPSfx *>::iterator i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ for (;;) {
+ // Check each active sound
+ for (_ctx->i = g_vm->_activeSfx.begin(); _ctx->i != g_vm->_activeSfx.end(); ++_ctx->i) {
+ FPSfx *sfx = *_ctx->i;
+ if (sfx->endOfBuffer())
+ CoroScheduler.setEvent(sfx->_hEndOfBuffer);
+ }
+
+ // Delay until the next check is done
+ CORO_INVOKE_1(CoroScheduler.sleep, 50);
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateStream()
+ */
+FPStream::FPStream(bool soundOn) {
+ _soundSupported = soundOn;
+ _fileLoaded = false;
+ _paused = false;
+ _loop = false;
+ _doFadeOut = false;
+ _syncExit = false;
+ _bufferSize = _size = 0;
+ _lastVolume = 0;
+ _syncToPlay = NULL;
+ _loopStream = NULL;
+ _rewindableStream = NULL;
+}
+
+/**
+ * Default destructor.
+ *
+ * @remarks It calls CloseFile() if needed.
+ */
+
+FPStream::~FPStream() {
+ if (!_soundSupported)
+ return;
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ stop();
+
+ if (_fileLoaded)
+ unloadFile();
+
+ _syncToPlay = NULL;
+}
+
+/**
+ * Releases the memory object.
+ *
+ * @remarks Must be called when the object is no longer used
+ * and **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+void FPStream::release() {
+ delete this;
+}
+
+/**
+ * Opens a file stream
+ *
+ * @param fileName Filename to be opened
+ * @param codec CODEC to be used to uncompress samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+bool FPStream::loadFile(const Common::String &fileName, uint32 codec, int bufSize) {
+ if (!_soundSupported)
+ return true;
+
+ if (_fileLoaded)
+ unloadFile();
+
+ // Save the codec type
+ _codec = codec;
+
+ // Open the file stream for reading
+ if (!_file.open(fileName)) {
+ // Fallback: try with an extra '0' prefix
+ if (!_file.open("0" + fileName))
+ return false;
+ }
+
+ // Save the size of the stream
+ _size = _file.size();
+
+ switch (_codec) {
+ case FPCODEC_RAW:
+ _rewindableStream = Audio::makeRawStream(&_file, 44100, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO, DisposeAfterUse::NO);
+ break;
+
+ case FPCODEC_ADPCM:
+ _rewindableStream = Audio::makeADPCMStream(&_file, DisposeAfterUse::NO, 0, Audio::kADPCMDVI, 44100, 2);
+ break;
+
+ default:
+ _file.close();
+ return false;
+ }
+
+ // All done
+ _fileLoaded = true;
+ _paused = false;
+
+ setVolume(63);
+
+ return true;
+}
+
+/**
+ * Closes a file stream (opened or not).
+ *
+ * @returns For safety, the destructor calls unloadFile() if it has not
+ * been mentioned explicitly.
+ *
+ * @remarks It is necessary to call this function to free the
+ * memory used by the stream.
+ */
+bool FPStream::unloadFile() {
+ if (!_soundSupported || !_fileLoaded)
+ return true;
+
+ assert(!g_system->getMixer()->isSoundHandleActive(_handle));
+
+ // Closes the file handle stream
+ delete _loopStream;
+ delete _rewindableStream;
+ _loopStream = NULL;
+ _rewindableStream = NULL;
+ _file.close();
+
+ // Flag that the file is no longer in memory
+ _fileLoaded = false;
+
+ return true;
+}
+
+/**
+ * Play the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPStream::play() {
+ if (!_soundSupported || !_fileLoaded)
+ return false;
+
+ stop();
+
+ _rewindableStream->rewind();
+
+ Audio::AudioStream *stream = _rewindableStream;
+
+ if (_loop) {
+ if (!_loopStream)
+ _loopStream = new Audio::LoopingAudioStream(_rewindableStream, 0, DisposeAfterUse::NO);
+
+ stream = _loopStream;
+ }
+
+ // FIXME: Should this be kMusicSoundType or KPlainSoundType?
+ g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+ setVolume(_lastVolume);
+ _paused = false;
+
+ return true;
+}
+
+/**
+ * Closes the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ *
+ */
+
+bool FPStream::stop() {
+ if (!_soundSupported)
+ return true;
+
+ if (!_fileLoaded)
+ return false;
+
+ if (!g_system->getMixer()->isSoundHandleActive(_handle))
+ return false;
+
+ g_system->getMixer()->stopHandle(_handle);
+
+ _paused = false;
+
+ return true;
+}
+
+void FPStream::waitForSync(FPStream *toPlay) {
+ // FIXME: The idea here is that you wait for this stream to reach
+ // a buffer which is a multiple of nBufSize/nSync, and then the
+ // thread stops it and immediately starts the 'toplay' stream.
+
+ stop();
+ toPlay->play();
+}
+
+/**
+ * Unables or disables stream loop.
+ *
+ * @param loop True enable loop, False disables it
+ *
+ * @remarks The loop must be activated BEFORE the stream starts
+ * playing. Any changes made during the play will have no
+ * effect until the stream is stopped then played again.
+ */
+void FPStream::setLoop(bool loop) {
+ _loop = loop;
+}
+
+/**
+ * Pause sound effect
+ *
+ * @param pause True enables pause, False disables it
+ */
+void FPStream::setPause(bool pause) {
+ if (!_fileLoaded)
+ return;
+
+ if (pause == _paused)
+ return;
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->pauseHandle(_handle, pause);
+
+ _paused = pause;
+
+ // Trick to reset the volume after a possible new sound configuration
+ setVolume(_lastVolume);
+}
+
+/**
+ * Change the volume of the stream
+ *
+ * @param volume Volume to be set (0-63)
+ *
+ */
+
+void FPStream::setVolume(int volume) {
+ if (volume > 63)
+ volume = 63;
+
+ if (volume < 0)
+ volume = 0;
+
+ _lastVolume = volume;
+
+ if (!GLOBALS._bCfgMusic)
+ volume = 0;
+ else {
+ volume -= (10 - GLOBALS._nCfgMusicVolume) * 2;
+ if (volume < 0)
+ volume = 0;
+ }
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->setChannelVolume(_handle, volume * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Gets the volume of the stream
+ *
+ * @param volumePtr Variable that will contain the current volume
+ *
+ */
+
+void FPStream::getVolume(int *volumePtr) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ *volumePtr = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume;
+ else
+ *volumePtr = 0;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/sound.h b/engines/tony/sound.h
new file mode 100644
index 0000000000..7422de02b3
--- /dev/null
+++ b/engines/tony/sound.h
@@ -0,0 +1,377 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_SOUND_H
+#define TONY_SOUND_H
+
+#include "audio/mixer.h"
+#include "common/file.h"
+#include "tony/gfxcore.h"
+#include "tony/loc.h"
+#include "tony/utils.h"
+
+namespace Audio {
+class RewindableAudioStream;
+}
+
+namespace Tony {
+
+class FPStream;
+class FPSfx;
+
+enum SoundCodecs {
+ FPCODEC_RAW,
+ FPCODEC_ADPCM
+};
+
+/**
+ * Sound driver For Tony Tough
+ */
+
+class FPSound {
+private:
+ bool _soundSupported;
+
+public:
+ /**
+ * Default constructor. Initializes the attributes.
+ *
+ */
+
+ FPSound();
+
+ /**
+ * Destroy the object and free the memory
+ *
+ */
+
+ ~FPSound();
+
+ /**
+ * Initializes the object, and prepare everything you need to create streams and sound effects.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool init();
+
+ /**
+ * Allocates an object of type FPStream, and return its pointer
+ *
+ * @param streamPtr Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool createStream(FPStream **streamPtr);
+
+ /**
+ * Allocates an object of type FpSfx, and return its pointer
+ *
+ * @param sfxPtr Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool createSfx(FPSfx **sfxPtr);
+
+ /**
+ * Set the general volume
+ *
+ * @param volume Volume to set (0-63)
+ */
+
+ void setMasterVolume(int volume);
+
+ /**
+ * Get the general volume
+ *
+ * @param volume Variable that will contain the volume (0-63)
+ */
+
+ void getMasterVolume(int *volume);
+};
+
+class FPSfx {
+private:
+ bool _soundSupported; // True if the sound is active
+ bool _fileLoaded; // True is a file is opened
+ bool _loop; // True is sound effect should loop
+ int _lastVolume;
+
+ bool _isVoice;
+ bool _paused;
+
+ Audio::AudioStream *_loopStream;
+ Audio::RewindableAudioStream *_rewindableStream;
+ Audio::SoundHandle _handle;
+
+public:
+ uint32 _hEndOfBuffer;
+
+ /**
+ * Check process for whether sounds have finished playing
+ */
+ static void soundCheckProcess(CORO_PARAM, const void *param);
+
+ /**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateSfx()
+ *
+ */
+
+ FPSfx(bool soundOn);
+
+ /**
+ * Default Destructor.
+ *
+ * @remarks It is also stops the sound effect that may be
+ * currently played, and free the memory it uses.
+ *
+ */
+
+ ~FPSfx();
+
+ /**
+ * Releases the memory used by the object.
+ *
+ * @remarks Must be called when the object is no longer used and
+ * **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+
+ void release();
+
+ /**
+ * Opens a file and loads a sound effect.
+ *
+ * @param fileName Sfx filename
+ * @param codec CODEC used to uncompress the samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool loadFile(const char *fileName, uint32 codec = FPCODEC_RAW);
+ bool loadWave(Common::SeekableReadStream *stream);
+ bool loadVoiceFromVDB(Common::File &vdbFP);
+
+ /**
+ * Play the Sfx in memory.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool play();
+
+ /**
+ * Stops a Sfx.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool stop();
+
+ /**
+ * Pauses a Sfx.
+ *
+ */
+
+ void setPause(bool pause);
+
+ /**
+ * Enables or disables the Sfx loop.
+ *
+ * @param loop True to enable the loop, False to disable
+ *
+ * @remarks The loop must be activated BEFORE the sfx starts
+ * playing. Any changes made during the play will have
+ * no effect until the sfx is stopped then played again.
+ */
+
+ void setLoop(bool loop);
+
+ /**
+ * Change the volume of Sfx
+ *
+ * @param volume Volume to be set (0-63)
+ *
+ */
+
+ void setVolume(int volume);
+
+ /**
+ * Gets the Sfx volume
+ *
+ * @param volumePtr Will contain the current Sfx volume
+ *
+ */
+
+ void getVolume(int *volumePtr);
+
+ /**
+ * Returns true if the underlying sound has ended
+ */
+ bool endOfBuffer() const;
+};
+
+class FPStream {
+private:
+ uint32 _bufferSize; // Buffer size (bytes)
+ uint32 _size; // Stream size (bytes)
+ uint32 _codec; // CODEC used
+
+ Common::File _file; // File handle used for the stream
+
+ bool _soundSupported; // True if the sound is active
+ bool _fileLoaded; // True if the file is open
+ bool _loop; // True if the stream should loop
+ bool _doFadeOut; // True if fade out is required
+ bool _syncExit;
+ bool _paused;
+ int _lastVolume;
+ FPStream *_syncToPlay;
+
+ Audio::AudioStream *_loopStream;
+ Audio::RewindableAudioStream *_rewindableStream;
+ Audio::SoundHandle _handle;
+
+public:
+
+ /**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateStream()
+ */
+
+ FPStream(bool soundOn);
+
+ /**
+ * Default destructor.
+ *
+ * @remarks It calls CloseFile() if needed.
+ */
+
+ ~FPStream();
+
+ /**
+ * Releases the memory object.
+ *
+ * @remarks Must be called when the object is no longer used
+ * and **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+
+ void release();
+
+ /**
+ * Opens a file stream
+ *
+ * @param fileName Filename to be opened
+ * @param codec CODEC to be used to uncompress samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool loadFile(const Common::String &fileName, uint32 codec = FPCODEC_RAW, int sync = 2000);
+
+ /**
+ * Closes a file stream (opened or not).
+ *
+ * @returns For safety, the destructor calls unloadFile() if it has not
+ * been mentioned explicitly.
+ *
+ * @remarks It is necessary to call this function to free the
+ * memory used by the stream.
+ */
+
+ bool unloadFile();
+
+ /**
+ * Play the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool play();
+ void playFast();
+
+ /**
+ * Closes the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool stop();
+ void waitForSync(FPStream *toPlay);
+
+ /**
+ * Pause sound effect
+ *
+ * @param pause True enables pause, False disables it
+ */
+
+ void setPause(bool pause);
+
+ /**
+ * Unables or disables stream loop.
+ *
+ * @param loop True enable loop, False disables it
+ *
+ * @remarks The loop must be activated BEFORE the stream starts
+ * playing. Any changes made during the play will have no
+ * effect until the stream is stopped then played again.
+ */
+
+ void setLoop(bool loop);
+
+ /**
+ * Change the volume of the stream
+ *
+ * @param volume Volume to be set (0-63)
+ */
+
+ void setVolume(int volume);
+
+ /**
+ * Gets the volume of the stream
+ *
+ * @param volumePtr Variable that will contain the current volume
+ *
+ */
+
+ void getVolume(int *volumePtr);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/tony.cpp b/engines/tony/tony.cpp
new file mode 100644
index 0000000000..1c63096e92
--- /dev/null
+++ b/engines/tony/tony.cpp
@@ -0,0 +1,791 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/algorithm.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/installshield_cab.h"
+#include "tony/tony.h"
+#include "tony/custom.h"
+#include "tony/debugger.h"
+#include "tony/game.h"
+#include "tony/mpal/mpal.h"
+
+namespace Tony {
+
+TonyEngine *g_vm;
+
+TonyEngine::TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc) : Engine(syst),
+ _gameDescription(gameDesc), _randomSource("tony") {
+ g_vm = this;
+ _loadSlotNumber = -1;
+
+ // Set the up the debugger
+ _debugger = new Debugger();
+ DebugMan.addDebugChannel(kTonyDebugAnimations, "animations", "Animations debugging");
+ DebugMan.addDebugChannel(kTonyDebugActions, "actions", "Actions debugging");
+ DebugMan.addDebugChannel(kTonyDebugSound, "sound", "Sound debugging");
+ DebugMan.addDebugChannel(kTonyDebugMusic, "music", "Music debugging");
+
+ // Add folders to the search directory list
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "Voices");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "Roasted");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "Music");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "Music/utilsfx");
+
+ // Set up load slot number
+ _initialLoadSlotNumber = -1;
+ if (ConfMan.hasKey("save_slot")) {
+ int slotNumber = ConfMan.getInt("save_slot");
+ if (slotNumber >= 0 && slotNumber <= 99)
+ _initialLoadSlotNumber = slotNumber;
+ }
+
+ // Load the ScummVM sound settings
+ syncSoundSettings();
+
+ _hEndOfFrame = 0;
+ for (int i = 0; i < 6; i++)
+ _stream[i] = NULL;
+ for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
+ _sfx[i] = NULL;
+ _utilSfx[i] = NULL;
+ }
+ _bPaused = false;
+ _bDrawLocation = false;
+ _startTime = 0;
+ _curThumbnail = NULL;
+ _bQuitNow = false;
+ _bTimeFreezed = false;
+ _nTimeFreezed = 0;
+}
+
+TonyEngine::~TonyEngine() {
+ // Close the voice database
+ closeVoiceDatabase();
+
+ // Reset the coroutine scheduler
+ CoroScheduler.reset();
+ CoroScheduler.setResourceCallback(NULL);
+
+ delete _debugger;
+}
+
+/**
+ * Run the game
+ */
+Common::Error TonyEngine::run() {
+ Common::ErrorCode result = init();
+ if (result != Common::kNoError)
+ return result;
+
+ play();
+ close();
+
+ return Common::kNoError;
+}
+
+/**
+ * Initialize the game
+ */
+Common::ErrorCode TonyEngine::init() {
+ // Load DAT file (used by font manager)
+ if (!loadTonyDat())
+ return Common::kUnknownError;
+
+ if (isCompressed()) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
+ if (!stream)
+ error("Failed to open data1.cab");
+
+ Common::Archive *cabinet = Common::makeInstallShieldArchive(stream);
+ if (!cabinet)
+ error("Failed to parse data1.cab");
+
+ SearchMan.add("data1.cab", cabinet);
+ }
+
+ _hEndOfFrame = CoroScheduler.createEvent(false, false);
+
+ _bPaused = false;
+ _bDrawLocation = true;
+ _startTime = g_system->getMillis();
+
+ // Init static class fields
+ RMText::initStatics();
+ RMTony::initStatics();
+
+ // Reset the scheduler
+ CoroScheduler.reset();
+
+ // Initialize the graphics window
+ _window.init();
+
+ // Initialize the function list
+ Common::fill(_funcList, _funcList + 300, (LPCUSTOMFUNCTION)NULL);
+ initCustomFunctionMap();
+
+ // Initializes MPAL system, passing the custom functions list
+ Common::File f;
+ if (!f.open("ROASTED.MPC"))
+ return Common::kReadingFailed;
+ f.close();
+
+ if (!mpalInit("ROASTED.MPC", "ROASTED.MPR", _funcList, _funcListStrings))
+ return Common::kUnknownError;
+
+ // Initialize the update resources
+ _resUpdate.init("ROASTED.MPU");
+
+ // Initialize the music
+ initMusic();
+
+ // Initialize the voices database
+ if (!openVoiceDatabase())
+ return Common::kReadingFailed;
+
+ // Initialize the boxes
+ _theBoxes.init();
+
+ // Link to the custom graphics engine
+ _theEngine.initCustomDll();
+ _theEngine.init();
+
+ // Allocate space for thumbnails when saving the game
+ _curThumbnail = new uint16[160 * 120];
+
+ _bQuitNow = false;
+
+ return Common::kNoError;
+}
+
+bool TonyEngine::loadTonyDat() {
+ Common::String msg;
+ Common::File in;
+
+ in.open("tony.dat");
+
+ if (!in.isOpen()) {
+ msg = "You're missing the 'tony.dat' file. Get it from the ScummVM website";
+ GUIErrorMessage(msg);
+ warning("%s", msg.c_str());
+ return false;
+ }
+
+ // Read header
+ char buf[4+1];
+ in.read(buf, 4);
+ buf[4] = '\0';
+
+ if (strcmp(buf, "TONY")) {
+ msg = "File 'tony.dat' is corrupt. Get it from the ScummVM website";
+ GUIErrorMessage(msg);
+ warning("%s", msg.c_str());
+ return false;
+ }
+
+ int majVer = in.readByte();
+ int minVer = in.readByte();
+
+ if ((majVer != TONY_DAT_VER_MAJ) || (minVer != TONY_DAT_VER_MIN)) {
+ msg = Common::String::format("File 'tony.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", TONY_DAT_VER_MAJ, TONY_DAT_VER_MIN, majVer, minVer);
+ GUIErrorMessage(msg);
+ warning("%s", msg.c_str());
+
+ return false;
+ }
+
+ int expectedLangVariant = -1;
+ switch (g_vm->getLanguage()) {
+ case Common::IT_ITA:
+ case Common::EN_ANY:
+ expectedLangVariant = 0;
+ break;
+ case Common::PL_POL:
+ expectedLangVariant = 1;
+ break;
+ case Common::RU_RUS:
+ expectedLangVariant = 2;
+ break;
+ case Common::CZ_CZE:
+ expectedLangVariant = 3;
+ break;
+ case Common::FR_FRA:
+ expectedLangVariant = 4;
+ break;
+ case Common::DE_DEU:
+ expectedLangVariant = 5;
+ break;
+ default:
+ warning("Unhandled language, falling back to English/Italian fonts.");
+ expectedLangVariant = 0;
+ break;
+ }
+
+ int numVariant = in.readUint16BE();
+ if (expectedLangVariant > numVariant - 1) {
+ msg = Common::String::format("Font variant not present in 'tony.dat'. Get it from the ScummVM website");
+ GUIErrorMessage(msg);
+ warning("%s", msg.c_str());
+
+ return false;
+ }
+
+ in.seek(in.pos() + (2 * 256 * 8 * expectedLangVariant));
+ for (int i = 0; i < 256; i++) {
+ _cTableDialog[i] = in.readSint16BE();
+ _lTableDialog[i] = in.readSint16BE();
+ _cTableMacc[i] = in.readSint16BE();
+ _lTableMacc[i] = in.readSint16BE();
+ _cTableCred[i] = in.readSint16BE();
+ _lTableCred[i] = in.readSint16BE();
+ _cTableObj[i] = in.readSint16BE();
+ _lTableObj[i] = in.readSint16BE();
+ }
+
+ return true;
+}
+
+void TonyEngine::initCustomFunctionMap() {
+ INIT_CUSTOM_FUNCTION(_funcList, _funcListStrings);
+}
+
+/**
+ * Display an error message
+ */
+void TonyEngine::GUIError(const Common::String &msg) {
+ GUIErrorMessage(msg);
+}
+
+void TonyEngine::playMusic(int nChannel, const Common::String &fname, int nFX, bool bLoop, int nSync) {
+ if (nChannel < 4) {
+ if (GLOBALS._flipflop)
+ nChannel = nChannel + 1;
+ }
+
+ switch (nFX) {
+ case 0:
+ case 1:
+ case 2:
+ _stream[nChannel]->stop();
+ _stream[nChannel]->unloadFile();
+ break;
+
+ case 22:
+ break;
+ }
+
+ if (nFX == 22) { // Sync a tempo
+ GLOBALS._curChannel = nChannel;
+ GLOBALS._nextLoop = bLoop;
+ GLOBALS._nextSync = nSync;
+ GLOBALS._nextMusic = fname;
+
+ if (GLOBALS._flipflop)
+ GLOBALS._nextChannel = nChannel - 1;
+ else
+ GLOBALS._nextChannel = nChannel + 1;
+
+ uint32 hThread = CoroScheduler.createProcess(doNextMusic, NULL, 0);
+ assert(hThread != CORO_INVALID_PID_VALUE);
+
+ } else if (nFX == 44) { // Change the channel and let the first finish
+ if (GLOBALS._flipflop)
+ GLOBALS._nextChannel = nChannel - 1;
+ else
+ GLOBALS._nextChannel = nChannel + 1;
+
+ _stream[GLOBALS._nextChannel]->stop();
+ _stream[GLOBALS._nextChannel]->unloadFile();
+
+ if (!getIsDemo()) {
+ if (!_stream[GLOBALS._nextChannel]->loadFile(fname, FPCODEC_ADPCM, nSync))
+ error("failed to open music file '%s'", fname.c_str());
+ } else {
+ _stream[GLOBALS._nextChannel]->loadFile(fname, FPCODEC_ADPCM, nSync);
+ }
+
+ _stream[GLOBALS._nextChannel]->setLoop(bLoop);
+ _stream[GLOBALS._nextChannel]->play();
+
+ GLOBALS._flipflop = 1 - GLOBALS._flipflop;
+ } else {
+ if (!getIsDemo()) {
+ if (!_stream[nChannel]->loadFile(fname, FPCODEC_ADPCM, nSync))
+ error("failed to open music file '%s'", fname.c_str());
+ } else {
+ _stream[nChannel]->loadFile(fname, FPCODEC_ADPCM, nSync);
+ }
+
+ _stream[nChannel]->setLoop(bLoop);
+ _stream[nChannel]->play();
+ }
+}
+
+void TonyEngine::doNextMusic(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ Common::String fn;
+ CORO_END_CONTEXT(_ctx);
+
+ FPStream **streams = g_vm->_stream;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!g_vm->getIsDemo()) {
+ if (!streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, FPCODEC_ADPCM, GLOBALS._nextSync))
+ error("failed to open next music file '%s'", GLOBALS._nextMusic.c_str());
+ } else {
+ streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, FPCODEC_ADPCM, GLOBALS._nextSync);
+ }
+
+ streams[GLOBALS._nextChannel]->setLoop(GLOBALS._nextLoop);
+ //streams[GLOBALS._nextChannel]->prefetch();
+
+ streams[GLOBALS._curChannel]->waitForSync(streams[GLOBALS._nextChannel]);
+
+ streams[GLOBALS._curChannel]->unloadFile();
+
+ GLOBALS._flipflop = 1 - GLOBALS._flipflop;
+
+ CORO_END_CODE;
+}
+
+void TonyEngine::playSFX(int nChannel, int nFX) {
+ if (_sfx[nChannel] == NULL)
+ return;
+
+ switch (nFX) {
+ case 0:
+ _sfx[nChannel]->setLoop(false);
+ break;
+
+ case 1:
+ _sfx[nChannel]->setLoop(true);
+ break;
+ }
+
+ _sfx[nChannel]->play();
+}
+
+void TonyEngine::stopMusic(int nChannel) {
+ if (nChannel < 4)
+ _stream[nChannel + GLOBALS._flipflop]->stop();
+ else
+ _stream[nChannel]->stop();
+}
+
+void TonyEngine::stopSFX(int nChannel) {
+ _sfx[nChannel]->stop();
+}
+
+void TonyEngine::playUtilSFX(int nChannel, int nFX) {
+ if (_utilSfx[nChannel] == NULL)
+ return;
+
+ switch (nFX) {
+ case 0:
+ _utilSfx[nChannel]->setLoop(false);
+ break;
+
+ case 1:
+ _utilSfx[nChannel]->setLoop(true);
+ break;
+ }
+
+ _utilSfx[nChannel]->setVolume(52);
+ _utilSfx[nChannel]->play();
+}
+
+void TonyEngine::stopUtilSFX(int nChannel) {
+ _utilSfx[nChannel]->stop();
+}
+
+void TonyEngine::preloadSFX(int nChannel, const char *fn) {
+ if (_sfx[nChannel] != NULL) {
+ _sfx[nChannel]->stop();
+ _sfx[nChannel]->release();
+ _sfx[nChannel] = NULL;
+ }
+
+ _theSound.createSfx(&_sfx[nChannel]);
+
+ _sfx[nChannel]->loadFile(fn, FPCODEC_ADPCM);
+}
+
+FPSfx *TonyEngine::createSFX(Common::SeekableReadStream *stream) {
+ FPSfx *sfx;
+
+ _theSound.createSfx(&sfx);
+ sfx->loadWave(stream);
+ return sfx;
+}
+
+void TonyEngine::preloadUtilSFX(int nChannel, const char *fn) {
+ if (_utilSfx[nChannel] != NULL) {
+ _utilSfx[nChannel]->stop();
+ _utilSfx[nChannel]->release();
+ _utilSfx[nChannel] = NULL;
+ }
+
+ _theSound.createSfx(&_utilSfx[nChannel]);
+
+ _utilSfx[nChannel]->loadFile(fn, FPCODEC_ADPCM);
+ _utilSfx[nChannel]->setVolume(63);
+}
+
+void TonyEngine::unloadAllSFX() {
+ for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
+ if (_sfx[i] != NULL) {
+ _sfx[i]->stop();
+ _sfx[i]->release();
+ _sfx[i] = NULL;
+ }
+ }
+}
+
+void TonyEngine::unloadAllUtilSFX() {
+ for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
+ if (_utilSfx[i] != NULL) {
+ _utilSfx[i]->stop();
+ _utilSfx[i]->release();
+ _utilSfx[i] = NULL;
+ }
+ }
+}
+
+void TonyEngine::initMusic() {
+ int i;
+
+ _theSound.init();
+ _theSound.setMasterVolume(63);
+
+ for (i = 0; i < 6; i++)
+ _theSound.createStream(&_stream[i]);
+
+ for (i = 0; i < MAX_SFX_CHANNELS; i++) {
+ _sfx[i] = _utilSfx[i] = NULL;
+ }
+
+ // Preload sound effects
+ preloadUtilSFX(0, "U01.ADP"); // Reversed!!
+ preloadUtilSFX(1, "U02.ADP");
+
+ // Start check processes for sound
+ CoroScheduler.createProcess(FPSfx::soundCheckProcess, NULL);
+}
+
+void TonyEngine::closeMusic() {
+ for (int i = 0; i < 6; i++) {
+ _stream[i]->stop();
+ _stream[i]->unloadFile();
+ _stream[i]->release();
+ }
+
+ unloadAllSFX();
+ unloadAllUtilSFX();
+}
+
+void TonyEngine::pauseSound(bool bPause) {
+ _theEngine.pauseSound(bPause);
+
+ for (uint i = 0; i < 6; i++)
+ if (_stream[i])
+ _stream[i]->setPause(bPause);
+
+ for (uint i = 0; i < MAX_SFX_CHANNELS; i++) {
+ if (_sfx[i])
+ _sfx[i]->setPause(bPause);
+ if (_utilSfx[i])
+ _utilSfx[i]->setPause(bPause);
+ }
+}
+
+void TonyEngine::setMusicVolume(int nChannel, int volume) {
+ _stream[nChannel + GLOBALS._flipflop]->setVolume(volume);
+}
+
+int TonyEngine::getMusicVolume(int nChannel) {
+ int volume;
+ _stream[nChannel + GLOBALS._flipflop]->getVolume(&volume);
+ return volume;
+}
+
+Common::String TonyEngine::getSaveStateFileName(int n) {
+ return Common::String::format("tony.%03d", n);
+}
+
+void TonyEngine::autoSave(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ Common::String buf;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ grabThumbnail();
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ _ctx->buf = getSaveStateFileName(0);
+ _theEngine.saveState(_ctx->buf, (byte *)_curThumbnail, "Autosave");
+
+ CORO_END_CODE;
+}
+
+void TonyEngine::saveState(int n, const char *name) {
+ Common::String buf = getSaveStateFileName(n);
+ _theEngine.saveState(buf.c_str(), (byte *)_curThumbnail, name);
+}
+
+void TonyEngine::loadState(CORO_PARAM, int n) {
+ CORO_BEGIN_CONTEXT;
+ Common::String buf;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->buf = getSaveStateFileName(n);
+ CORO_INVOKE_1(_theEngine.loadState, _ctx->buf.c_str());
+
+ CORO_END_CODE;
+}
+
+bool TonyEngine::openVoiceDatabase() {
+ char id[4];
+ uint32 numfiles;
+
+ // Open the voices database
+ if (!_vdbFP.open("voices.vdb"))
+ return false;
+
+ _vdbFP.seek(-8, SEEK_END);
+ numfiles = _vdbFP.readUint32LE();
+ _vdbFP.read(id, 4);
+
+ if (id[0] != 'V' || id[1] != 'D' || id[2] != 'B' || id[3] != '1') {
+ _vdbFP.close();
+ return false;
+ }
+
+ // Read in the index
+ _vdbFP.seek(-8 - (numfiles * VOICE_HEADER_SIZE), SEEK_END);
+
+ for (uint32 i = 0; i < numfiles; ++i) {
+ VoiceHeader vh;
+ vh._offset = _vdbFP.readUint32LE();
+ vh._code = _vdbFP.readUint32LE();
+ vh._parts = _vdbFP.readUint32LE();
+
+ _voices.push_back(vh);
+ }
+
+ return true;
+}
+
+void TonyEngine::closeVoiceDatabase() {
+ if (_vdbFP.isOpen())
+ _vdbFP.close();
+
+ if (_voices.size() > 0)
+ _voices.clear();
+}
+
+void TonyEngine::grabThumbnail() {
+ _window.grabThumbnail(_curThumbnail);
+}
+
+uint16 *TonyEngine::getThumbnail() {
+ return _curThumbnail;
+}
+
+void TonyEngine::quitGame() {
+ _bQuitNow = true;
+}
+
+void TonyEngine::openInitLoadMenu(CORO_PARAM) {
+ _theEngine.openOptionScreen(coroParam, 1);
+}
+
+void TonyEngine::openInitOptions(CORO_PARAM) {
+ _theEngine.openOptionScreen(coroParam, 2);
+}
+
+/**
+ * Main process for playing the game.
+ *
+ * @remarks This needs to be in a separate process, since there are some things that can briefly
+ * block the execution of process. For now, all ScummVm event handling is dispatched to within the context of this
+ * process. If it ever proves a problem, we may have to look into whether it's feasible to have it still remain
+ * in the outer 'main' process.
+ */
+void TonyEngine::playProcess(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ Common::String fn;
+ CORO_END_CONTEXT(_ctx);
+
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Game loop. We rely on the outer main process to detect if a shutdown is required,
+ // and kill the scheudler and all the processes, including this one
+ for (;;) {
+ // If a savegame needs to be loaded, then do so
+ if (g_vm->_loadSlotNumber != -1 && GLOBALS._gfxEngine != NULL) {
+ _ctx->fn = getSaveStateFileName(g_vm->_loadSlotNumber);
+ CORO_INVOKE_1(GLOBALS._gfxEngine->loadState, _ctx->fn);
+ g_vm->_loadSlotNumber = -1;
+ }
+
+ // Wait for the next frame
+ CORO_INVOKE_1(CoroScheduler.sleep, 50);
+
+ // Call the engine to handle the next frame
+ CORO_INVOKE_1(g_vm->_theEngine.doFrame, g_vm->_bDrawLocation);
+
+ // Warns that a frame is finished
+ CoroScheduler.pulseEvent(g_vm->_hEndOfFrame);
+
+ // Handle drawing the frame
+ if (!g_vm->_bPaused) {
+ if (!g_vm->_theEngine._bWiping)
+ g_vm->_window.getNewFrame(g_vm->_theEngine, NULL);
+ else
+ g_vm->_window.getNewFrame(g_vm->_theEngine, &g_vm->_theEngine._rcWipeEllipse);
+ }
+
+ // Paint the frame onto the screen
+ g_vm->_window.repaint();
+
+ // Signal the ScummVM debugger
+ g_vm->_debugger->onFrame();
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Play the game
+ */
+void TonyEngine::play() {
+ // Create the game player process
+ CoroScheduler.createProcess(playProcess, NULL);
+
+ // Loop through calling the scheduler until it's time for the game to quit
+ while (!shouldQuit() && !_bQuitNow) {
+ // Delay for a brief amount
+ g_system->delayMillis(10);
+
+ // Call any scheduled processes
+ CoroScheduler.schedule();
+ }
+}
+
+void TonyEngine::close() {
+ closeMusic();
+ CoroScheduler.closeEvent(_hEndOfFrame);
+ _theBoxes.close();
+ _theEngine.close();
+ _window.close();
+ mpalFree();
+ freeMpc();
+ delete[] _curThumbnail;
+}
+
+void TonyEngine::freezeTime() {
+ _bTimeFreezed = true;
+ _nTimeFreezed = getTime() - _startTime;
+}
+
+void TonyEngine::unfreezeTime() {
+ _bTimeFreezed = false;
+}
+
+/**
+ * Returns the millisecond timer
+ */
+uint32 TonyEngine::getTime() {
+ return g_system->getMillis();
+}
+
+bool TonyEngine::canLoadGameStateCurrently() {
+ return GLOBALS._gfxEngine != NULL && GLOBALS._gfxEngine->canLoadSave();
+}
+bool TonyEngine::canSaveGameStateCurrently() {
+ return GLOBALS._gfxEngine != NULL && GLOBALS._gfxEngine->canLoadSave();
+}
+
+Common::Error TonyEngine::loadGameState(int slot) {
+ _loadSlotNumber = slot;
+ return Common::kNoError;
+}
+
+Common::Error TonyEngine::saveGameState(int slot, const Common::String &desc) {
+ if (!GLOBALS._gfxEngine)
+ return Common::kUnknownError;
+
+ RMGfxTargetBuffer &bigBuf = *GLOBALS._gfxEngine;
+ RMSnapshot s;
+ s.grabScreenshot(bigBuf, 4, _curThumbnail);
+
+ GLOBALS._gfxEngine->saveState(getSaveStateFileName(slot), (byte *)_curThumbnail, desc);
+ return Common::kNoError;
+}
+
+void TonyEngine::syncSoundSettings() {
+ Engine::syncSoundSettings();
+
+ GLOBALS._bCfgDubbing = !ConfMan.getBool("mute") && !ConfMan.getBool("speech_mute");
+ GLOBALS._bCfgSFX = !ConfMan.getBool("mute") && !ConfMan.getBool("sfx_mute");
+ GLOBALS._bCfgMusic = !ConfMan.getBool("mute") && !ConfMan.getBool("music_mute");
+
+ GLOBALS._nCfgDubbingVolume = ConfMan.getInt("speech_volume") * 10 / 256;
+ GLOBALS._nCfgSFXVolume = ConfMan.getInt("sfx_volume") * 10 / 256;
+ GLOBALS._nCfgMusicVolume = ConfMan.getInt("music_volume") * 10 / 256;
+
+ GLOBALS._bShowSubtitles = ConfMan.getBool("subtitles");
+ GLOBALS._nCfgTextSpeed = ConfMan.getInt("talkspeed") * 10 / 256;
+}
+
+void TonyEngine::saveSoundSettings() {
+ ConfMan.setBool("speech_mute", !GLOBALS._bCfgDubbing);
+ ConfMan.setBool("sfx_mute", !GLOBALS._bCfgSFX);
+ ConfMan.setBool("music_mute", !GLOBALS._bCfgMusic);
+
+ ConfMan.setInt("speech_volume", GLOBALS._nCfgDubbingVolume * 256 / 10);
+ ConfMan.setInt("sfx_volume", GLOBALS._nCfgSFXVolume * 256 / 10);
+ ConfMan.setInt("music_volume", GLOBALS._nCfgMusicVolume * 256 / 10);
+
+ ConfMan.setBool("subtitles", GLOBALS._bShowSubtitles);
+ ConfMan.setInt("talkspeed", GLOBALS._nCfgTextSpeed * 256 / 10);
+}
+
+void TonyEngine::showLocation() {
+ _bDrawLocation = true;
+}
+
+void TonyEngine::hideLocation() {
+ _bDrawLocation = false;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/tony.h b/engines/tony/tony.h
new file mode 100644
index 0000000000..332b122923
--- /dev/null
+++ b/engines/tony/tony.h
@@ -0,0 +1,242 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef TONY_H
+#define TONY_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/array.h"
+#include "common/coroutines.h"
+#include "common/error.h"
+#include "common/random.h"
+#include "common/util.h"
+#include "engines/engine.h"
+
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/memory.h"
+#include "tony/debugger.h"
+#include "tony/gfxengine.h"
+#include "tony/loc.h"
+#include "tony/utils.h"
+#include "tony/window.h"
+#include "tony/globals.h"
+
+/**
+ * This is the namespace of the Tony engine.
+ *
+ * Status of this engine: In Development
+ *
+ * Games using this engine:
+ * - Tony Tough
+ */
+namespace Tony {
+
+using namespace MPAL;
+
+class Globals;
+
+enum {
+ kTonyDebugAnimations = 1 << 0,
+ kTonyDebugActions = 1 << 1,
+ kTonyDebugSound = 1 << 2,
+ kTonyDebugMusic = 1 << 3,
+ kTonyDebugMPAL = 1 << 4
+};
+
+#define DEBUG_BASIC 1
+#define DEBUG_INTERMEDIATE 2
+#define DEBUG_DETAILED 3
+
+struct TonyGameDescription;
+
+#define MAX_SFX_CHANNELS 32
+#define TONY_DAT_VER_MAJ 0
+#define TONY_DAT_VER_MIN 3
+
+struct VoiceHeader {
+ int _offset;
+ int _code;
+ int _parts;
+};
+#define VOICE_HEADER_SIZE 12
+
+class TonyEngine : public Engine {
+private:
+ Common::ErrorCode init();
+ bool loadTonyDat();
+ void initMusic();
+ void closeMusic();
+ bool openVoiceDatabase();
+ void closeVoiceDatabase();
+ void initCustomFunctionMap();
+ static void playProcess(CORO_PARAM, const void *param);
+ static void doNextMusic(CORO_PARAM, const void *param);
+
+protected:
+ // Engine APIs
+ virtual Common::Error run();
+ virtual bool hasFeature(EngineFeature f) const;
+public:
+ LPCUSTOMFUNCTION _funcList[300];
+ Common::String _funcListStrings[300];
+ Common::RandomSource _randomSource;
+ RMResUpdate _resUpdate;
+ uint32 _hEndOfFrame;
+ Common::File _vdbFP;
+ Common::Array<VoiceHeader> _voices;
+ FPSound _theSound;
+ Common::List<FPSfx *> _activeSfx;
+ Globals _globals;
+ Debugger *_debugger;
+
+ int16 _cTableDialog[256];
+ int16 _lTableDialog[256];
+ int16 _cTableMacc[256];
+ int16 _lTableMacc[256];
+ int16 _cTableCred[256];
+ int16 _lTableCred[256];
+ int16 _cTableObj[256];
+ int16 _lTableObj[256];
+
+ enum DataDir {
+ DD_BASE = 1,
+ DD_SAVE,
+ DD_SHOTS,
+ DD_MUSIC,
+ DD_LAYER,
+ DD_UTILSFX,
+ DD_VOICES,
+ DD_BASE2
+ };
+
+ FPStream *_stream[6];
+ FPSfx *_sfx[MAX_SFX_CHANNELS];
+ FPSfx *_utilSfx[MAX_SFX_CHANNELS];
+ bool _bPaused;
+ bool _bDrawLocation;
+ int _startTime;
+ uint16 *_curThumbnail;
+ int _initialLoadSlotNumber;
+ int _loadSlotNumber;
+
+ // Bounding box list manager
+ RMGameBoxes _theBoxes;
+ RMWindow _window;
+ RMGfxEngine _theEngine;
+
+ bool _bQuitNow;
+ bool _bTimeFreezed;
+ int _nTimeFreezed;
+public:
+ TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc);
+ virtual ~TonyEngine();
+
+ const TonyGameDescription *_gameDescription;
+ uint32 getFeatures() const;
+ Common::Language getLanguage() const;
+ uint16 getVersion() const;
+ bool getIsDemo() const;
+ bool isCompressed() const;
+ RMGfxEngine *getEngine() {
+ return &_theEngine;
+ }
+ void GUIError(const Common::String &msg);
+
+ virtual bool canLoadGameStateCurrently();
+ virtual bool canSaveGameStateCurrently();
+ Common::Error loadGameState(int slot);
+ Common::Error saveGameState(int slot, const Common::String &desc);
+
+ void play();
+ void close();
+
+ void getDataDirectory(DataDir dir, char *path);
+
+ void showLocation();
+ void hideLocation();
+
+ /**
+ * Reads the time
+ */
+ uint32 getTime();
+ void freezeTime();
+ void unfreezeTime();
+
+ // Music
+ // ******
+ void playMusic(int nChannel, const Common::String &fn, int nFX, bool bLoop, int nSync);
+ void stopMusic(int nChannel);
+
+ void playSFX(int nSfx, int nFX = 0);
+ void stopSFX(int nSfx);
+
+ void playUtilSFX(int nSfx, int nFX = 0);
+ void stopUtilSFX(int nSfx);
+
+ FPSfx *createSFX(Common::SeekableReadStream *stream);
+
+ void preloadSFX(int nSfx, const char *fn);
+ void unloadAllSFX();
+
+ void preloadUtilSFX(int nSfx, const char *fn);
+ void unloadAllUtilSFX();
+
+ /**
+ * Stop all the audio
+ */
+ void pauseSound(bool bPause);
+
+ void setMusicVolume(int nChannel, int volume);
+ int getMusicVolume(int nChannel);
+
+ /**
+ * Handle saving
+ */
+ void autoSave(CORO_PARAM);
+ void saveState(int n, const char *name);
+ void loadState(CORO_PARAM, int n);
+ static Common::String getSaveStateFileName(int n);
+
+ /**
+ * Get a thumbnail
+ */
+ void grabThumbnail();
+ uint16 *getThumbnail();
+
+ void quitGame();
+
+ void openInitLoadMenu(CORO_PARAM);
+ void openInitOptions(CORO_PARAM);
+
+ virtual void syncSoundSettings();
+ void saveSoundSettings();
+};
+
+// Global reference to the TonyEngine object
+extern TonyEngine *g_vm;
+
+#define GLOBALS g_vm->_globals
+
+} // End of namespace Tony
+
+#endif /* TONY_H */
diff --git a/engines/tony/tonychar.cpp b/engines/tony/tonychar.cpp
new file mode 100644
index 0000000000..c7fa1e4a7b
--- /dev/null
+++ b/engines/tony/tonychar.cpp
@@ -0,0 +1,1945 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/mpal/memory.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/game.h"
+#include "tony/tonychar.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+bool RMTony::_bAction = false;
+
+void RMTony::initStatics() {
+ _bAction = false;
+}
+
+RMTony::RMTony() {
+ _bShow = false;
+ _bShowShadow = false;
+ _bBodyFront = false;
+ _bActionPending = false;
+ _actionItem = NULL;
+ _action = 0;
+ _actionParm = 0;
+ _bShepherdess = false;
+ _bIsStaticTalk = false;
+ _bIsTalking = false;
+ _nPatB4Talking = 0;
+ _nTalkType = TALK_NORMAL;
+ _talkDirection = UP;
+ _nTimeLastStep = 0;
+ _hActionThread = CORO_INVALID_PID_VALUE;
+}
+
+void RMTony::waitEndOfAction(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 pid = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE);
+
+ _bAction = false;
+
+ CORO_END_CODE;
+}
+
+RMGfxSourceBuffer *RMTony::newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE) {
+ RMGfxSourceBuffer8RLE *spr;
+
+ assert(_cm == CM_256);
+ spr = new RMGfxSourceBuffer8RLEByteAA;
+ spr->setAlphaBlendColor(1);
+ if (bPreRLE)
+ spr->setAlreadyCompressed();
+ return spr;
+}
+
+void RMTony::init() {
+ RMRes tony(0);
+ RMRes body(9999);
+
+ // Tony is shown by default
+ _bShow = _bShowShadow = true;
+
+ // No action pending
+ _bActionPending = false;
+ _bAction = false;
+
+ _bShepherdess = false;
+ _bIsTalking = false;
+ _bIsStaticTalk = false;
+
+ // Opens the buffer
+ Common::SeekableReadStream *ds = tony.getReadStream();
+
+ // Reads his details from the stream
+ readFromStream(*ds, true);
+
+ // Closes the buffer
+ delete ds;
+
+ // Reads Tony's body
+ ds = body.getReadStream();
+ _body.readFromStream(*ds, true);
+ delete ds;
+ _body.setPattern(0);
+
+ _nTimeLastStep = g_vm->getTime();
+}
+
+
+void RMTony::close() {
+ // Deallocation of missing item
+// _shadow.destroy();
+}
+
+void RMTony::doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int curLoc) {
+ CORO_BEGIN_CONTEXT;
+ int time;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!_nInList && _bShow)
+ bigBuf->addPrim(new RMGfxPrimitive(this));
+
+ setSpeed(GLOBALS._nCfgTonySpeed);
+
+ // Runs the normal character movement
+ _ctx->time = g_vm->getTime();
+
+ do {
+ _nTimeLastStep += (1000 / 40);
+ CORO_INVOKE_2(RMCharacter::doFrame, bigBuf, curLoc);
+ } while (_ctx->time > _nTimeLastStep + (1000 / 40));
+
+ // Check if we are at the end of a path
+ if (endOfPath() && _bActionPending) {
+ // Must perform the action on which we clicked
+ _bActionPending = false;
+ }
+
+ if (_bIsTalking || _bIsStaticTalk)
+ _body.doFrame(bigBuf, false);
+
+ CORO_END_CODE;
+}
+
+void RMTony::show() {
+ _bShow = true;
+ _bShowShadow = true;
+}
+
+void RMTony::hide(bool bShowShadow) {
+ _bShow = false;
+ _bShowShadow = bShowShadow;
+}
+
+
+void RMTony::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Call the Draw() of the parent class if Tony is visible
+ if (_bShow && _bDrawNow) {
+ if (_bBodyFront) {
+ prim->getDst().setEmpty();
+ prim->getDst().offset(-44, -134);
+ if (_bShepherdess)
+ prim->getDst().offset(1, 4);
+ CORO_INVOKE_2(RMCharacter::draw, bigBuf, prim);
+ }
+
+ if (_bIsTalking || _bIsStaticTalk) {
+ // Offest direction from scrolling
+ prim->getDst().setEmpty();
+ prim->getDst().offset(-_curScroll);
+ prim->getDst().offset(_pos);
+ prim->getDst().offset(-44, -134);
+ prim->getDst() += _nBodyOffset;
+ CORO_INVOKE_2(_body.draw, bigBuf, prim);
+ }
+
+ if (!_bBodyFront) {
+ prim->getDst().setEmpty();
+ prim->getDst().offset(-44, -134);
+ if (_bShepherdess)
+ prim->getDst().offset(0, 3);
+ CORO_INVOKE_2(RMCharacter::draw, bigBuf, prim);
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMTony::moveAndDoAction(CORO_PARAM, RMPoint dst, RMItem *item, int nAction, int nActionParm) {
+ CORO_BEGIN_CONTEXT;
+ bool result;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Makes normal movement, but remember if you must then perform an action
+ if (item == NULL) {
+ _bActionPending = false;
+ _actionItem = NULL;
+ } else {
+ _actionItem = item;
+ _action = nAction;
+ _actionParm = nActionParm;
+ _bActionPending = true;
+ }
+
+ CORO_INVOKE_2(RMCharacter::move, dst, &_ctx->result);
+ if (!_ctx->result) {
+ _bActionPending = false;
+ _actionItem = NULL;
+ }
+
+ CORO_END_CODE;
+}
+
+
+void RMTony::executeAction(int nAction, int nActionItem, int nParm) {
+ uint32 pid;
+
+ if (nAction == TA_COMBINE) {
+ pid = mpalQueryDoAction(TA_COMBINE, nParm, nActionItem);
+
+ // If you failed the combine, we have RECEIVECOMBINE as a fallback
+ if (pid == CORO_INVALID_PID_VALUE) {
+ pid = mpalQueryDoAction(TA_RECEIVECOMBINE, nActionItem, nParm);
+
+ // If you failed with that, go with the generic
+ // @@@ CombineGive!
+ if (pid == CORO_INVALID_PID_VALUE) {
+ pid = mpalQueryDoAction(TA_COMBINE, nParm, 0);
+
+ if (pid == CORO_INVALID_PID_VALUE) {
+ pid = mpalQueryDoAction(TA_RECEIVECOMBINE, nActionItem, 0);
+ }
+ }
+ }
+ } else {
+ // Perform the action
+ pid = mpalQueryDoAction(nAction, nActionItem, 0);
+ }
+
+ if (pid != CORO_INVALID_PID_VALUE) {
+ _bAction = true;
+ CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32));
+ _hActionThread = pid;
+ } else if (nAction != TA_GOTO) {
+ if (nAction == TA_TALK) {
+ pid = mpalQueryDoAction(6, 1, 0);
+ _bAction = true;
+ CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32));
+ _hActionThread = pid;
+ } else if (nAction == TA_PERORATE) {
+ pid = mpalQueryDoAction(7, 1, 0);
+ _bAction = true;
+ CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32));
+ _hActionThread = pid;
+ } else {
+ pid = mpalQueryDoAction(5, 1, 0);
+ _bAction = true;
+ CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32));
+ _hActionThread = pid;
+ }
+ }
+}
+
+
+void RMTony::stopNoAction(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bAction)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hActionThread, CORO_INFINITE);
+
+ _bActionPending = false;
+ _actionItem = NULL;
+ CORO_INVOKE_0(stop);
+
+ CORO_END_CODE;
+}
+
+void RMTony::stop(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ uint32 pid;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_actionItem != NULL) {
+ // Call MPAL to choose the direction
+ _ctx->pid = mpalQueryDoAction(21, _actionItem->mpalCode(), 0);
+
+ if (_ctx->pid == CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_0(RMCharacter::stop);
+ else {
+ _bNeedToStop = false; // If we make the OnWhichDirection, we don't need at least after the Stop().
+ _bMoving = false;
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->pid, CORO_INFINITE); // @@@ Put an assert after 10 seconds
+ }
+ } else {
+ CORO_INVOKE_0(RMCharacter::stop);
+ }
+
+ if (!_bActionPending)
+ return;
+
+ _bActionPending = false;
+
+ executeAction(_action, _actionItem->mpalCode(), _actionParm);
+
+ _actionItem = NULL;
+
+ CORO_END_CODE;
+}
+
+
+int RMTony::getCurPattern() {
+ int nPatt = RMCharacter::getCurPattern();
+
+ if (!_bShepherdess)
+ return nPatt;
+
+ switch (nPatt) {
+ case PAT_PAST_STANDUP:
+ return PAT_STANDUP;
+ case PAT_PAST_STANDDOWN:
+ return PAT_STANDDOWN;
+ case PAT_PAST_STANDLEFT:
+ return PAT_STANDLEFT;
+ case PAT_PAST_STANDRIGHT:
+ return PAT_STANDRIGHT;
+ case PAT_PAST_WALKUP:
+ return PAT_WALKUP;
+ case PAT_PAST_WALKDOWN:
+ return PAT_WALKDOWN;
+ case PAT_PAST_WALKLEFT:
+ return PAT_WALKLEFT;
+ case PAT_PAST_WALKRIGHT:
+ return PAT_WALKRIGHT;
+ }
+
+ return nPatt;
+}
+
+void RMTony::setPattern(int nPatt, bool bPlayP0) {
+ if (_bShepherdess) {
+ switch (nPatt) {
+ case PAT_STANDUP:
+ nPatt = PAT_PAST_STANDUP;
+ break;
+ case PAT_STANDDOWN:
+ nPatt = PAT_PAST_STANDDOWN;
+ break;
+ case PAT_STANDLEFT:
+ nPatt = PAT_PAST_STANDLEFT;
+ break;
+ case PAT_STANDRIGHT:
+ nPatt = PAT_PAST_STANDRIGHT;
+ break;
+ case PAT_WALKUP:
+ nPatt = PAT_PAST_WALKUP;
+ break;
+ case PAT_WALKDOWN:
+ nPatt = PAT_PAST_WALKDOWN;
+ break;
+ case PAT_WALKLEFT:
+ nPatt = PAT_PAST_WALKLEFT;
+ break;
+ case PAT_WALKRIGHT:
+ nPatt = PAT_PAST_WALKRIGHT;
+ break;
+ }
+ }
+
+ RMCharacter::setPattern(nPatt, bPlayP0);
+}
+
+
+void RMTony::take(int nWhere, int nPart) {
+ if (nPart == 0) {
+ switch (getCurPattern()) {
+ case PAT_STANDDOWN:
+ assert(0); // Not while you're doing a StandDown
+ break;
+
+ case PAT_STANDUP:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_TAKEUP_UP1);
+ break;
+ case 1:
+ setPattern(PAT_TAKEUP_MID1);
+ break;
+ case 2:
+ setPattern(PAT_TAKEUP_DOWN1);
+ break;
+ }
+ break;
+
+ case PAT_STANDRIGHT:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_TAKERIGHT_UP1);
+ break;
+ case 1:
+ setPattern(PAT_TAKERIGHT_MID1);
+ break;
+ case 2:
+ setPattern(PAT_TAKERIGHT_DOWN1);
+ break;
+ }
+ break;
+
+ case PAT_STANDLEFT:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_TAKELEFT_UP1);
+ break;
+ case 1:
+ setPattern(PAT_TAKELEFT_MID1);
+ break;
+ case 2:
+ setPattern(PAT_TAKELEFT_DOWN1);
+ break;
+ }
+ break;
+ }
+ } else if (nPart == 1) {
+ setPattern(getCurPattern() + 1);
+ } else if (nPart == 2) {
+ switch (getCurPattern()) {
+ case PAT_TAKEUP_UP2:
+ case PAT_TAKEUP_MID2:
+ case PAT_TAKEUP_DOWN2:
+ setPattern(PAT_STANDUP);
+ break;
+
+ case PAT_TAKELEFT_UP2:
+ case PAT_TAKELEFT_MID2:
+ case PAT_TAKELEFT_DOWN2:
+ setPattern(PAT_STANDLEFT);
+ break;
+
+ case PAT_TAKERIGHT_UP2:
+ case PAT_TAKERIGHT_MID2:
+ case PAT_TAKERIGHT_DOWN2:
+ setPattern(PAT_STANDRIGHT);
+ break;
+ }
+ }
+}
+
+
+void RMTony::put(int nWhere, int nPart) {
+ if (nPart == 0) {
+ switch (getCurPattern()) {
+ case PAT_STANDDOWN:
+ break;
+
+ case PAT_STANDUP:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_PUTUP_UP1);
+ break;
+ case 1:
+ setPattern(PAT_PUTUP_MID1);
+ break;
+ case 2:
+ setPattern(PAT_PUTUP_DOWN1);
+ break;
+ }
+ break;
+
+ case PAT_STANDRIGHT:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_PUTRIGHT_UP1);
+ break;
+ case 1:
+ setPattern(PAT_PUTRIGHT_MID1);
+ break;
+ case 2:
+ setPattern(PAT_PUTRIGHT_DOWN1);
+ break;
+ }
+ break;
+
+ case PAT_STANDLEFT:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_PUTLEFT_UP1);
+ break;
+ case 1:
+ setPattern(PAT_PUTLEFT_MID1);
+ break;
+ case 2:
+ setPattern(PAT_PUTLEFT_DOWN1);
+ break;
+ }
+ break;
+ }
+ } else if (nPart == 1) {
+ setPattern(getCurPattern() + 1);
+ } else if (nPart == 2) {
+ switch (getCurPattern()) {
+ case PAT_PUTUP_UP2:
+ case PAT_PUTUP_MID2:
+ case PAT_PUTUP_DOWN2:
+ setPattern(PAT_STANDUP);
+ break;
+
+ case PAT_PUTLEFT_UP2:
+ case PAT_PUTLEFT_MID2:
+ case PAT_PUTLEFT_DOWN2:
+ setPattern(PAT_STANDLEFT);
+ break;
+
+ case PAT_PUTRIGHT_UP2:
+ case PAT_PUTRIGHT_MID2:
+ case PAT_PUTRIGHT_DOWN2:
+ setPattern(PAT_STANDRIGHT);
+ break;
+ }
+ }
+}
+
+
+bool RMTony::startTalkCalculate(CharacterTalkType nTalkType, int &headStartPat, int &bodyStartPat,
+ int &headLoopPat, int &bodyLoopPat) {
+ assert(!_bIsTalking);
+
+ _bIsTalking = true;
+ _nPatB4Talking = getCurPattern();
+ _nTalkType = nTalkType;
+
+ // Set the direction of speech ONLY if we are not in a static animation (since it would have already been done)
+ if (!_bIsStaticTalk) {
+ switch (_nPatB4Talking) {
+ case PAT_STANDDOWN:
+ _talkDirection = DOWN;
+ break;
+
+ case PAT_TAKELEFT_UP2:
+ case PAT_TAKELEFT_MID2:
+ case PAT_TAKELEFT_DOWN2:
+ case PAT_GETUPLEFT:
+ case PAT_STANDLEFT:
+ _talkDirection = LEFT;
+ break;
+
+ case PAT_TAKERIGHT_UP2:
+ case PAT_TAKERIGHT_MID2:
+ case PAT_TAKERIGHT_DOWN2:
+ case PAT_GETUPRIGHT:
+ case PAT_STANDRIGHT:
+ _talkDirection = RIGHT;
+ break;
+
+ case PAT_TAKEUP_UP2:
+ case PAT_TAKEUP_MID2:
+ case PAT_TAKEUP_DOWN2:
+ case PAT_STANDUP:
+ _talkDirection = UP;
+ break;
+ }
+
+ // Puts the body in front by default
+ _bBodyFront = true;
+ }
+
+ if (_bShepherdess) {
+ // Talking whilst a shepherdess
+ switch (_talkDirection) {
+ case UP:
+ setPattern(PAT_PAST_TALKUP);
+ break;
+
+ case DOWN:
+ setPattern(PAT_PAST_TALKDOWN);
+ break;
+
+ case LEFT:
+ setPattern(PAT_PAST_TALKLEFT);
+ break;
+
+ case RIGHT:
+ setPattern(PAT_PAST_TALKRIGHT);
+ break;
+ }
+ return false;
+ }
+
+ headStartPat = bodyStartPat = 0;
+ bodyLoopPat = 0;
+
+ switch (nTalkType) {
+ case TALK_NORMAL:
+ _bBodyFront = false;
+ headStartPat = 0;
+ bodyStartPat = 0;
+
+ switch (_talkDirection) {
+ case DOWN:
+ headLoopPat = PAT_TALK_DOWN;
+ bodyLoopPat = BPAT_STANDDOWN;
+ _nBodyOffset.set(4, 53);
+ break;
+
+ case LEFT:
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_STANDLEFT;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ case RIGHT:
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ case UP:
+ headLoopPat = PAT_TALK_UP;
+ bodyLoopPat = BPAT_STANDUP;
+ _nBodyOffset.set(6, 53);
+ break;
+ }
+ break;
+
+ case TALK_HIPS:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case UP:
+ _nBodyOffset.set(2, 42);
+ headStartPat = PAT_HEAD_UP;
+ bodyStartPat = BPAT_HIPSUP_START;
+ headLoopPat = PAT_TALK_UP;
+ bodyLoopPat = BPAT_HIPSUP_LOOP;
+ break;
+
+ case DOWN:
+ _nBodyOffset.set(2, 48);
+ headStartPat = PAT_HEAD_DOWN;
+ bodyStartPat = BPAT_HIPSDOWN_START;
+ headLoopPat = PAT_TALK_DOWN;
+ bodyLoopPat = BPAT_HIPSDOWN_LOOP;
+ break;
+
+ case LEFT:
+ _nBodyOffset.set(-3, 53);
+ headStartPat = PAT_HEAD_LEFT;
+ bodyStartPat = BPAT_HIPSLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_HIPSLEFT_LOOP;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(2, 53);
+ headStartPat = PAT_HEAD_RIGHT;
+ bodyStartPat = BPAT_HIPSRIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_HIPSRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_SING:
+ _nBodyOffset.set(-10, 25);
+ headStartPat = PAT_HEAD_LEFT;
+ bodyStartPat = BPAT_SINGLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_SINGLEFT_LOOP;
+ break;
+
+ case TALK_LAUGH:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case UP:
+ case DOWN:
+ case LEFT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_LAUGHLEFT_START;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_LAUGHLEFT_LOOP;
+ bodyLoopPat = BPAT_LAUGHLEFT;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_LAUGHRIGHT_START;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_LAUGHRIGHT_LOOP;
+ bodyLoopPat = BPAT_LAUGHRIGHT;
+ break;
+ }
+ break;
+
+ case TALK_LAUGH2:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case UP:
+ case DOWN:
+ case LEFT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_LAUGHLEFT_START;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_LAUGHLEFT_LOOP;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_LAUGHRIGHT_START;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_LAUGHRIGHT_LOOP;
+ bodyLoopPat = BPAT_LAUGHRIGHT;
+ break;
+ }
+ break;
+
+ case TALK_INDICATE:
+ switch (_talkDirection) {
+ case UP:
+ case DOWN:
+ case LEFT:
+ _nBodyOffset.set(-4, 40);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_INDICATELEFT;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(5, 40);
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_INDICATERIGHT;
+ break;
+ }
+ break;
+
+ case TALK_SCARED:
+ switch (_talkDirection) {
+ case UP:
+ _nBodyOffset.set(-4, -11);
+ headStartPat = PAT_HEAD_UP;
+ bodyStartPat = BPAT_SCAREDUP_START;
+ headLoopPat = PAT_TALK_UP;
+ bodyLoopPat = BPAT_SCAREDUP_LOOP;
+ break;
+
+ case DOWN:
+ _nBodyOffset.set(-5, 45);
+ headStartPat = PAT_SCAREDDOWN_START;
+ bodyStartPat = BPAT_SCAREDDOWN_START;
+ headLoopPat = PAT_SCAREDDOWN_LOOP;
+ bodyLoopPat = BPAT_SCAREDDOWN_LOOP;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(-4, 41);
+ headStartPat = PAT_SCAREDRIGHT_START;
+ bodyStartPat = BPAT_SCAREDRIGHT_START;
+ headLoopPat = PAT_SCAREDRIGHT_LOOP;
+ bodyLoopPat = BPAT_SCAREDRIGHT_LOOP;
+ break;
+
+ case LEFT:
+ _nBodyOffset.set(-10, 41);
+ headStartPat = PAT_SCAREDLEFT_START;
+ bodyStartPat = BPAT_SCAREDLEFT_START;
+ headLoopPat = PAT_SCAREDLEFT_LOOP;
+ bodyLoopPat = BPAT_SCAREDLEFT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_SCARED2:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case UP:
+ bodyStartPat = BPAT_STANDUP;
+ bodyLoopPat = BPAT_STANDUP;
+ _nBodyOffset.set(6, 53);
+
+ headStartPat = PAT_HEAD_UP;
+ headLoopPat = PAT_TALK_UP;
+ break;
+
+ case DOWN:
+ bodyStartPat = BPAT_STANDDOWN;
+ bodyLoopPat = BPAT_STANDDOWN;
+ _nBodyOffset.set(4, 53);
+
+ headStartPat = PAT_SCAREDDOWN_START;
+ headLoopPat = PAT_SCAREDDOWN_LOOP;
+ break;
+
+ case RIGHT:
+ bodyStartPat = BPAT_STANDRIGHT;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ _nBodyOffset.set(6, 56);
+
+ headStartPat = PAT_SCAREDRIGHT_START;
+ headLoopPat = PAT_SCAREDRIGHT_LOOP;
+ break;
+
+ case LEFT:
+ bodyStartPat = BPAT_STANDLEFT;
+ bodyLoopPat = BPAT_STANDLEFT;
+ _nBodyOffset.set(6, 56);
+
+ headStartPat = PAT_SCAREDLEFT_START;
+ headLoopPat = PAT_SCAREDLEFT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHGLASSES:
+ _nBodyOffset.set(4, 53);
+ headLoopPat = PAT_TALK_DOWN;
+ bodyLoopPat = BPAT_GLASS;
+ break;
+ case TALK_WITHWORM:
+ _nBodyOffset.set(9, 56);
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WORM;
+ break;
+ case TALK_WITHHAMMER:
+ _nBodyOffset.set(6, 56);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_HAMMER;
+ break;
+ case TALK_WITHROPE:
+ _nBodyOffset.set(-3, 38);
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_ROPE;
+ break;
+ case TALK_WITHSECRETARY:
+ _nBodyOffset.set(-17, 12);
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHSECRETARY;
+ break;
+
+ case TALK_WITHRABBIT:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(-21, -5);
+ bodyStartPat = BPAT_WITHRABBITLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_WITHRABBITLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-4, -5);
+ bodyStartPat = BPAT_WITHRABBITRIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHRABBITRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHRECIPE:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(-61, -7);
+ bodyStartPat = BPAT_WITHRECIPELEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_WITHRECIPELEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-5, -7);
+ bodyStartPat = BPAT_WITHRECIPERIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHRECIPERIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHCARDS:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(-34, -2);
+ bodyStartPat = BPAT_WITHCARDSLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_WITHCARDSLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-4, -2);
+ bodyStartPat = BPAT_WITHCARDSRIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHCARDSRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHSNOWMAN:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(-35, 2);
+ bodyStartPat = BPAT_WITHSNOWMANLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_WITHSNOWMANLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-14, 2);
+ bodyStartPat = BPAT_WITHSNOWMANRIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHSNOWMANRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHSNOWMANSTATIC:
+ case TALK_WITHRECIPESTATIC:
+ case TALK_WITHRABBITSTATIC:
+ case TALK_WITHCARDSSTATIC:
+ case TALK_WITH_NOTEBOOK:
+ case TALK_WITHMEGAPHONESTATIC:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ headLoopPat = PAT_TALK_LEFT;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ headLoopPat = PAT_TALK_RIGHT;
+ break;
+ }
+ break;
+
+ // The beard is the only case in which the head is animated separately while the body is the standard
+ case TALK_WITHBEARDSTATIC:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ headLoopPat = PAT_TALKBEARD_LEFT;
+ bodyLoopPat = BPAT_STANDLEFT;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ case DOWN:
+ case RIGHT:
+ headLoopPat = PAT_TALKBEARD_RIGHT;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ _nBodyOffset.set(6, 56);
+ break;
+ }
+ break;
+
+ case TALK_DISGUSTED:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_DISGUSTEDLEFT_START;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_DISGUSTEDLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_DISGUSTEDRIGHT_START;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_DISGUSTEDRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_SARCASTIC:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_SARCASTICLEFT_START;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_SARCASTICLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_SARCASTICRIGHT_START;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_SARCASTICRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_MACBETH1:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH1;
+ break;
+ case TALK_MACBETH2:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH2;
+ break;
+ case TALK_MACBETH3:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH3;
+ break;
+ case TALK_MACBETH4:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH4;
+ break;
+ case TALK_MACBETH5:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH5;
+ break;
+ case TALK_MACBETH6:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH6;
+ break;
+ case TALK_MACBETH7:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH7;
+ break;
+ case TALK_MACBETH8:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH8;
+ break;
+ case TALK_MACBETH9:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH9;
+ break;
+
+ case TALK_SCAREDSTATIC:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case DOWN:
+ bodyStartPat = BPAT_STANDDOWN;
+ bodyLoopPat = BPAT_STANDDOWN;
+ _nBodyOffset.set(4, 53);
+
+ headStartPat = PAT_SCAREDDOWN_STAND;
+ headLoopPat = PAT_SCAREDDOWN_LOOP;
+ break;
+
+ case RIGHT:
+ bodyStartPat = BPAT_STANDRIGHT;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ _nBodyOffset.set(6, 56);
+
+ headStartPat = PAT_SCAREDRIGHT_STAND;
+ headLoopPat = PAT_SCAREDRIGHT_LOOP;
+ break;
+
+ case LEFT:
+ bodyStartPat = BPAT_STANDLEFT;
+ bodyLoopPat = BPAT_STANDLEFT;
+ _nBodyOffset.set(6, 56);
+
+ headStartPat = PAT_SCAREDLEFT_STAND;
+ headLoopPat = PAT_SCAREDLEFT_LOOP;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ return true;
+}
+
+void RMTony::startTalk(CORO_PARAM, CharacterTalkType nTalkType) {
+ CORO_BEGIN_CONTEXT;
+ int headStartPat, bodyStartPat;
+ int headLoopPat, bodyLoopPat;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->headStartPat = _ctx->bodyStartPat = 0;
+ _ctx->headLoopPat = _ctx->bodyLoopPat = 0;
+
+ if (!startTalkCalculate(nTalkType, _ctx->headStartPat, _ctx->bodyStartPat,
+ _ctx->headLoopPat, _ctx->bodyLoopPat))
+ return;
+
+ // Perform the set pattern
+ if (_ctx->headStartPat != 0 || _ctx->bodyStartPat != 0) {
+ setPattern(_ctx->headStartPat);
+ _body.setPattern(_ctx->bodyStartPat);
+
+ if (_ctx->bodyStartPat != 0)
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ if (_ctx->headStartPat != 0)
+ CORO_INVOKE_0(waitForEndPattern);
+ }
+
+ setPattern(_ctx->headLoopPat);
+ if (_ctx->bodyLoopPat)
+ _body.setPattern(_ctx->bodyLoopPat);
+
+ CORO_END_CODE;
+}
+
+
+bool RMTony::endTalkCalculate(int &headStandPat, int &headEndPat, int &bodyEndPat, int &finalPat, bool &bStatic) {
+ bodyEndPat = 0;
+ headEndPat = 0;
+
+ switch (_talkDirection) {
+ case UP:
+ finalPat = PAT_STANDUP;
+ headStandPat = PAT_HEAD_UP;
+ break;
+
+ case DOWN:
+ finalPat = PAT_STANDDOWN;
+ headStandPat = PAT_HEAD_DOWN;
+ break;
+
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ headStandPat = PAT_HEAD_LEFT;
+ break;
+
+ case RIGHT:
+ finalPat = PAT_STANDRIGHT;
+ headStandPat = PAT_HEAD_RIGHT;
+ break;
+ }
+
+ if (_bShepherdess) {
+ setPattern(finalPat);
+ _bIsTalking = false;
+ return false;
+ }
+
+ bStatic = false;
+ switch (_nTalkType) {
+ case TALK_NORMAL:
+ bodyEndPat = 0;
+ break;
+
+ case TALK_HIPS:
+ switch (_talkDirection) {
+ case UP:
+ bodyEndPat = BPAT_HIPSUP_END;
+ break;
+
+ case DOWN:
+ bodyEndPat = BPAT_HIPSDOWN_END;
+ break;
+
+ case LEFT:
+ bodyEndPat = BPAT_HIPSLEFT_END;
+ break;
+
+ case RIGHT:
+ bodyEndPat = BPAT_HIPSRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_SING:
+ bodyEndPat = BPAT_SINGLEFT_END;
+ break;
+
+ case TALK_LAUGH:
+ case TALK_LAUGH2:
+ if (_talkDirection == LEFT)
+ headEndPat = PAT_LAUGHLEFT_END;
+ else if (_talkDirection == RIGHT)
+ headEndPat = PAT_LAUGHRIGHT_END;
+
+ bodyEndPat = 0;
+ break;
+
+ case TALK_DISGUSTED:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ headEndPat = PAT_DISGUSTEDLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ headEndPat = PAT_DISGUSTEDRIGHT_END;
+ break;
+ }
+
+ bodyEndPat = 0;
+ break;
+
+ case TALK_SARCASTIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ headEndPat = PAT_SARCASTICLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ headEndPat = PAT_SARCASTICRIGHT_END;
+ break;
+ }
+
+ bodyEndPat = 0;
+ break;
+
+ case TALK_INDICATE:
+ break;
+
+ case TALK_SCARED:
+ switch (_talkDirection) {
+ case UP:
+ bodyEndPat = BPAT_SCAREDUP_END;
+ break;
+
+ case DOWN:
+ headEndPat = PAT_SCAREDDOWN_END;
+ bodyEndPat = BPAT_SCAREDDOWN_END;
+ break;
+
+ case RIGHT:
+ headEndPat = PAT_SCAREDRIGHT_END;
+ bodyEndPat = BPAT_SCAREDRIGHT_END;
+ break;
+
+ case LEFT:
+ headEndPat = PAT_SCAREDLEFT_END;
+ bodyEndPat = BPAT_SCAREDLEFT_END;
+ break;
+ }
+ break;
+
+ case TALK_SCARED2:
+ switch (_talkDirection) {
+ case UP:
+ bodyEndPat = 0;
+ break;
+
+ case DOWN:
+ headEndPat = PAT_SCAREDDOWN_END;
+ bodyEndPat = 0;
+ break;
+
+ case RIGHT:
+ headEndPat = PAT_SCAREDRIGHT_END;
+ bodyEndPat = 0;
+ break;
+
+ case LEFT:
+ headEndPat = PAT_SCAREDLEFT_END;
+ bodyEndPat = 0;
+ break;
+ }
+ break;
+
+ case TALK_WITHRABBIT:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ bodyEndPat = BPAT_WITHRABBITLEFT_END;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ bodyEndPat = BPAT_WITHRABBITRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHRECIPE:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ bodyEndPat = BPAT_WITHRECIPELEFT_END;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ bodyEndPat = BPAT_WITHRECIPERIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHCARDS:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ bodyEndPat = BPAT_WITHCARDSLEFT_END;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ bodyEndPat = BPAT_WITHCARDSRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHSNOWMAN:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ bodyEndPat = BPAT_WITHSNOWMANLEFT_END;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ bodyEndPat = BPAT_WITHSNOWMANRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHWORM:
+ finalPat = PAT_WITHWORM;
+ break;
+ case TALK_WITHROPE:
+ finalPat = PAT_WITHROPE;
+ break;
+ case TALK_WITHSECRETARY:
+ finalPat = PAT_WITHSECRETARY;
+ break;
+ case TALK_WITHHAMMER:
+ finalPat = PAT_WITHHAMMER;
+ break;
+ case TALK_WITHGLASSES:
+ finalPat = PAT_WITHGLASSES;
+ break;
+
+ case TALK_MACBETH1:
+ case TALK_MACBETH2:
+ case TALK_MACBETH3:
+ case TALK_MACBETH4:
+ case TALK_MACBETH5:
+ case TALK_MACBETH6:
+ case TALK_MACBETH7:
+ case TALK_MACBETH8:
+ finalPat = 0;
+ break;
+
+ case TALK_SCAREDSTATIC:
+ switch (_talkDirection) {
+ case DOWN:
+ headStandPat = PAT_SCAREDDOWN_STAND;
+ bodyEndPat = 0;
+ break;
+
+ case RIGHT:
+ headStandPat = PAT_SCAREDRIGHT_STAND;
+ bodyEndPat = 0;
+ break;
+
+ case LEFT:
+ headStandPat = PAT_SCAREDLEFT_STAND;
+ bodyEndPat = 0;
+ break;
+
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void RMTony::endTalk(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ int headStandPat, headEndPat;
+ int bodyEndPat, finalPat;
+ bool bStatic;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->headStandPat = _ctx->headEndPat = 0;
+ _ctx->bodyEndPat = _ctx->finalPat = 0;
+ _ctx->bStatic = false;
+
+ _ctx->bodyEndPat = 0;
+ _ctx->headEndPat = 0;
+
+ if (!endTalkCalculate(_ctx->headStandPat, _ctx->headEndPat, _ctx->bodyEndPat, _ctx->finalPat, _ctx->bStatic))
+ return;
+
+ // Handles the end of an animated and static, leaving everything unchanged
+ if (_bIsStaticTalk) {
+ if (_nTalkType == TALK_WITHBEARDSTATIC) {
+ setPattern(0);
+ if (_talkDirection == UP || _talkDirection == LEFT) {
+ _body.setPattern(BPAT_WITHBEARDLEFT_STATIC);
+ _nBodyOffset.set(-41, -14);
+ } else if (_talkDirection == DOWN || _talkDirection == RIGHT) {
+ _body.setPattern(BPAT_WITHBEARDRIGHT_STATIC);
+ _nBodyOffset.set(-26, -14);
+ }
+ } else {
+ setPattern(_ctx->headStandPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ }
+
+ _bIsTalking = false;
+ return;
+ }
+
+ // Set the pattern
+ if (_ctx->headEndPat != 0 && _ctx->bodyEndPat != 0) {
+ setPattern(_ctx->headEndPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+
+ _body.setPattern(_ctx->bodyEndPat);
+
+ CORO_INVOKE_0(waitForEndPattern);
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ } else if (_ctx->bodyEndPat != 0) {
+ setPattern(_ctx->headStandPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+
+ _body.setPattern(_ctx->bodyEndPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ } else if (_ctx->headEndPat != 0) {
+ CORO_INVOKE_0(_body.waitForEndPattern);
+
+ setPattern(_ctx->headEndPat);
+
+ CORO_INVOKE_0(waitForEndPattern);
+ } else {
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ }
+
+ if (_ctx->finalPat != 0) {
+ _body.setPattern(0);
+ setPattern(_ctx->finalPat);
+ }
+
+ _bIsTalking = false;
+
+ CORO_END_CODE;
+}
+
+void RMTony::startStaticCalculate(CharacterTalkType nTalk, int &headPat, int &headLoopPat,
+ int &bodyStartPat, int &bodyLoopPat) {
+ int nPat = getCurPattern();
+
+ headLoopPat = -1;
+
+ switch (nPat) {
+ case PAT_STANDDOWN:
+ _talkDirection = DOWN;
+ headPat = PAT_HEAD_RIGHT;
+ break;
+
+ case PAT_TAKELEFT_UP2:
+ case PAT_TAKELEFT_MID2:
+ case PAT_TAKELEFT_DOWN2:
+ case PAT_GETUPLEFT:
+ case PAT_STANDLEFT:
+ _talkDirection = LEFT;
+ headPat = PAT_HEAD_LEFT;
+ break;
+
+ case PAT_TAKERIGHT_UP2:
+ case PAT_TAKERIGHT_MID2:
+ case PAT_TAKERIGHT_DOWN2:
+ case PAT_GETUPRIGHT:
+ case PAT_STANDRIGHT:
+ _talkDirection = RIGHT;
+ headPat = PAT_HEAD_RIGHT;
+ break;
+
+ case PAT_TAKEUP_UP2:
+ case PAT_TAKEUP_MID2:
+ case PAT_TAKEUP_DOWN2:
+ case PAT_STANDUP:
+ _talkDirection = UP;
+ headPat = PAT_HEAD_LEFT;
+ break;
+ }
+
+ _bBodyFront = true;
+
+ switch (nTalk) {
+ case TALK_WITHRABBITSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-21, -5);
+ bodyStartPat = BPAT_WITHRABBITLEFT_START;
+ bodyLoopPat = BPAT_WITHRABBITLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-4, -5);
+ bodyStartPat = BPAT_WITHRABBITRIGHT_START;
+ bodyLoopPat = BPAT_WITHRABBITRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHCARDSSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-34, -2);
+ bodyStartPat = BPAT_WITHCARDSLEFT_START;
+ bodyLoopPat = BPAT_WITHCARDSLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-4, -2);
+ bodyStartPat = BPAT_WITHCARDSRIGHT_START;
+ bodyLoopPat = BPAT_WITHCARDSRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHRECIPESTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-61, -7);
+ bodyStartPat = BPAT_WITHRECIPELEFT_START;
+ bodyLoopPat = BPAT_WITHRECIPELEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-5, -7);
+ bodyStartPat = BPAT_WITHRECIPERIGHT_START;
+ bodyLoopPat = BPAT_WITHRECIPERIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHSNOWMANSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-35, 2);
+ bodyStartPat = BPAT_WITHSNOWMANLEFT_START;
+ bodyLoopPat = BPAT_WITHSNOWMANLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-14, 2);
+ bodyStartPat = BPAT_WITHSNOWMANRIGHT_START;
+ bodyLoopPat = BPAT_WITHSNOWMANRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITH_NOTEBOOK:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-16, -9);
+ bodyStartPat = BPAT_WITHNOTEBOOKLEFT_START;
+ bodyLoopPat = BPAT_WITHNOTEBOOKLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-6, -9);
+ bodyStartPat = BPAT_WITHNOTEBOOKRIGHT_START;
+ bodyLoopPat = BPAT_WITHNOTEBOOKRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHMEGAPHONESTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-41, -8);
+ bodyStartPat = BPAT_WITHMEGAPHONELEFT_START;
+ bodyLoopPat = BPAT_WITHMEGAPHONELEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-14, -8);
+ bodyStartPat = BPAT_WITHMEGAPHONERIGHT_START;
+ bodyLoopPat = BPAT_WITHMEGAPHONERIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHBEARDSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-41, -14);
+ bodyStartPat = BPAT_WITHBEARDLEFT_START;
+ bodyLoopPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_TALKBEARD_LEFT;
+ headPat = 0;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-26, -14);
+ bodyStartPat = BPAT_WITHBEARDRIGHT_START;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_TALKBEARD_RIGHT;
+ headPat = 0;
+ break;
+ }
+ break;
+
+ case TALK_SCAREDSTATIC:
+ switch (_talkDirection) {
+ case DOWN:
+ headPat = PAT_SCAREDDOWN_START;
+ bodyLoopPat = BPAT_STANDDOWN;
+ bodyStartPat = BPAT_STANDDOWN;
+ headLoopPat = PAT_SCAREDDOWN_STAND;
+ _nBodyOffset.set(4, 53);
+ break;
+
+ case LEFT:
+ headPat = PAT_SCAREDLEFT_START;
+ bodyLoopPat = BPAT_STANDLEFT;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_SCAREDLEFT_STAND;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ case RIGHT:
+ headPat = PAT_SCAREDRIGHT_START;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_SCAREDRIGHT_STAND;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ default:
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void RMTony::startStatic(CORO_PARAM, CharacterTalkType nTalk) {
+ CORO_BEGIN_CONTEXT;
+ int headPat, headLoopPat;
+ int bodyStartPat, bodyLoopPat;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->headPat = _ctx->headLoopPat = 0;
+ _ctx->bodyStartPat = _ctx->bodyLoopPat = 0;
+
+ startStaticCalculate(nTalk, _ctx->headPat, _ctx->headLoopPat,
+ _ctx->bodyStartPat, _ctx->bodyLoopPat);
+
+ // e vai con i pattern
+ _bIsStaticTalk = true;
+
+ setPattern(_ctx->headPat);
+ _body.setPattern(_ctx->bodyStartPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ CORO_INVOKE_0(waitForEndPattern);
+
+ if (_ctx->headLoopPat != -1)
+ setPattern(_ctx->headLoopPat);
+ _body.setPattern(_ctx->bodyLoopPat);
+
+ CORO_END_CODE;
+}
+
+void RMTony::endStaticCalculate(CharacterTalkType nTalk, int &bodyEndPat, int &finalPat, int &headEndPat) {
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ break;
+ }
+
+ switch (nTalk) {
+ case TALK_WITHSNOWMANSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHSNOWMANLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHSNOWMANRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHRECIPESTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHRECIPELEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHRECIPERIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHRABBITSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHRABBITLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHRABBITRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHCARDSSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHCARDSLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHCARDSRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITH_NOTEBOOK:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHNOTEBOOKLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHNOTEBOOKRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHMEGAPHONESTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHMEGAPHONELEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHMEGAPHONERIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHBEARDSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHBEARDLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHBEARDRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_SCAREDSTATIC:
+ switch (_talkDirection) {
+ case LEFT:
+ headEndPat = PAT_SCAREDLEFT_END;
+ break;
+
+ case DOWN:
+ headEndPat = PAT_SCAREDDOWN_END;
+ break;
+
+ case RIGHT:
+ headEndPat = PAT_SCAREDRIGHT_END;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void RMTony::endStatic(CORO_PARAM, CharacterTalkType nTalk) {
+ CORO_BEGIN_CONTEXT;
+ int bodyEndPat;
+ int finalPat;
+ int headEndPat;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bodyEndPat = 0;
+ _ctx->finalPat = 0;
+ _ctx->headEndPat = 0;
+
+ endStaticCalculate(nTalk, _ctx->bodyEndPat, _ctx->finalPat, _ctx->headEndPat);
+
+ if (_ctx->headEndPat != 0) {
+ setPattern(_ctx->headEndPat);
+
+ CORO_INVOKE_0(waitForEndPattern);
+ } else {
+ // Play please
+ _body.setPattern(_ctx->bodyEndPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ }
+
+ setPattern(_ctx->finalPat);
+ _body.setPattern(0);
+
+ _bIsStaticTalk = false;
+
+ CORO_END_CODE;
+}
+
+/**
+ * Waits until the end of a pattern
+ */
+void RMTony::waitForEndPattern(CORO_PARAM, uint32 hCustomSkip) {
+ RMCharacter::waitForEndPattern(coroParam, hCustomSkip);
+}
+
+/**
+ * Check if currently in an action
+ */
+bool RMTony::inAction() {
+ return (_bActionPending && _action != 0) | _bAction;
+}
+
+/**
+ * Check if there needs to be an update for scrolling movement
+ */
+bool RMTony::mustUpdateScrolling() {
+ return ((!inAction()) || (isMoving()));
+}
+
+/**
+ * Returns Tony's position
+ */
+RMPoint RMTony::position() {
+ return _pos;
+}
+
+/**
+ * Set the scrolling position
+ */
+void RMTony::setScrollPosition(const RMPoint &pt) {
+ RMCharacter::setScrollPosition(pt);
+}
+
+/**
+ * Tony disguises himself!
+ */
+void RMTony::setShepherdess(bool bIsPast) {
+ _bShepherdess = bIsPast;
+}
+
+int RMTony::getShepherdess() {
+ return _bShepherdess;
+}
+
+void RMTony::playSfx(int nSfx) {
+ RMItem::playSfx(nSfx);
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/tonychar.h b/engines/tony/tonychar.h
new file mode 100644
index 0000000000..d9f18f61ec
--- /dev/null
+++ b/engines/tony/tonychar.h
@@ -0,0 +1,482 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_TONYCHAR_H
+#define TONY_TONYCHAR_H
+
+#include "common/coroutines.h"
+#include "tony/loc.h"
+
+namespace Tony {
+
+class RMTony : public RMCharacter {
+private:
+ enum CharacterDirection {
+ UP, DOWN, LEFT, RIGHT
+ };
+
+public:
+ enum CharacterTalkType {
+ TALK_NORMAL,
+ TALK_HIPS,
+ TALK_SING,
+ TALK_LAUGH,
+ TALK_INDICATE,
+ TALK_SCARED,
+ TALK_SCARED2,
+ TALK_WITHGLASSES,
+ TALK_WITHHAMMER,
+ TALK_WITHWORM,
+ TALK_WITHROPE,
+ TALK_WITHRABBIT,
+ TALK_WITHRECIPE,
+ TALK_WITHCARDS,
+ TALK_WITHSNOWMAN,
+ TALK_WITHSNOWMANSTATIC,
+ TALK_WITHRABBITSTATIC,
+ TALK_WITHRECIPESTATIC,
+ TALK_WITHCARDSSTATIC,
+ TALK_WITH_NOTEBOOK,
+ TALK_WITHMEGAPHONESTATIC,
+ TALK_WITHBEARDSTATIC,
+ TALK_LAUGH2,
+ TALK_DISGUSTED,
+ TALK_SARCASTIC,
+ TALK_MACBETH1,
+ TALK_MACBETH2,
+ TALK_MACBETH3,
+ TALK_MACBETH4,
+ TALK_MACBETH5,
+ TALK_MACBETH6,
+ TALK_MACBETH7,
+ TALK_MACBETH8,
+ TALK_MACBETH9,
+ TALK_SCAREDSTATIC,
+ TALK_WITHSECRETARY
+ };
+
+private:
+ bool _bShow;
+ bool _bShowShadow;
+ bool _bBodyFront;
+ // Useless variable?
+ // RMGfxSourceBuffer8AB _shadow;
+ bool _bActionPending;
+ RMItem *_actionItem;
+ int _action;
+ int _actionParm;
+ static bool _bAction;
+
+ bool _bShepherdess;
+
+ bool _bIsStaticTalk;
+ bool _bIsTalking;
+ int _nPatB4Talking;
+ CharacterTalkType _nTalkType;
+ CharacterDirection _talkDirection;
+ RMPoint _nBodyOffset;
+
+ int _nTimeLastStep;
+
+ RMItem _body;
+ uint32 _hActionThread;
+
+protected:
+ /**
+ * Overload of the allocation allocation of sprites
+ */
+ virtual RMGfxSourceBuffer *newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE);
+
+ /**
+ * Watch thread which waits for the end of an action
+ */
+ static void waitEndOfAction(CORO_PARAM, const void *param);
+
+public:
+ enum CharacterPatterns {
+ PAT_TAKEUP_UP1 = 9,
+ PAT_TAKEUP_UP2,
+ PAT_TAKEUP_MID1,
+ PAT_TAKEUP_MID2,
+ PAT_TAKEUP_DOWN1,
+ PAT_TAKEUP_DOWN2,
+
+ PAT_TAKELEFT_UP1,
+ PAT_TAKELEFT_UP2,
+ PAT_TAKELEFT_MID1,
+ PAT_TAKELEFT_MID2,
+ PAT_TAKELEFT_DOWN1,
+ PAT_TAKELEFT_DOWN2,
+
+ PAT_TAKERIGHT_UP1,
+ PAT_TAKERIGHT_UP2,
+ PAT_TAKERIGHT_MID1,
+ PAT_TAKERIGHT_MID2,
+ PAT_TAKERIGHT_DOWN1,
+ PAT_TAKERIGHT_DOWN2,
+
+ PAT_GETUPLEFT,
+ PAT_ONTHEFLOORLEFT,
+ PAT_GETUPRIGHT,
+ PAT_ONTHEFLOORRIGHT,
+
+ // Sheperdess!
+ PAT_PAST_WALKUP,
+ PAT_PAST_WALKDOWN,
+ PAT_PAST_WALKLEFT,
+ PAT_PAST_WALKRIGHT,
+
+ PAT_PAST_STANDUP,
+ PAT_PAST_STANDDOWN,
+ PAT_PAST_STANDLEFT,
+ PAT_PAST_STANDRIGHT,
+
+ // Speech
+ PAT_TALK_UP,
+ PAT_TALK_DOWN,
+ PAT_TALK_LEFT,
+ PAT_TALK_RIGHT,
+
+ // Static head
+ PAT_HEAD_UP,
+ PAT_HEAD_DOWN,
+ PAT_HEAD_LEFT,
+ PAT_HEAD_RIGHT,
+
+ // Laugh
+ PAT_LAUGHLEFT_START,
+ PAT_LAUGHLEFT_LOOP,
+ PAT_LAUGHLEFT_END,
+ PAT_LAUGHRIGHT_START,
+ PAT_LAUGHRIGHT_LOOP,
+ PAT_LAUGHRIGHT_END,
+
+ // Speaking as a shepherdess
+ PAT_PAST_TALKUP,
+ PAT_PAST_TALKDOWN,
+ PAT_PAST_TALKLEFT,
+ PAT_PAST_TALKRIGHT,
+
+ // Fear
+ PAT_SCAREDLEFT_START,
+ PAT_SCAREDLEFT_LOOP,
+ PAT_SCAREDLEFT_END,
+ PAT_SCAREDRIGHT_START,
+ PAT_SCAREDRIGHT_LOOP,
+ PAT_SCAREDRIGHT_END,
+ PAT_SCAREDDOWN_START,
+ PAT_SCAREDDOWN_LOOP,
+ PAT_SCAREDDOWN_END,
+
+ // With objects: full body
+ PAT_WITHGLASSES,
+ PAT_WITHROPE,
+ PAT_WITHWORM,
+ PAT_WITHHAMMER,
+
+ // Sound the whistle
+ PAT_WHISTLERIGHT,
+
+ // Head with beard
+ PAT_TALKBEARD_LEFT,
+ PAT_TALKBEARD_RIGHT,
+
+ // Sniff
+ PAT_SNIFF_LEFT,
+ PAT_SNIFF_RIGHT,
+
+ // Disgusted
+ PAT_DISGUSTEDLEFT_START,
+ PAT_DISGUSTEDLEFT_LOOP,
+ PAT_DISGUSTEDLEFT_END,
+ PAT_DISGUSTEDRIGHT_START,
+ PAT_DISGUSTEDRIGHT_LOOP,
+ PAT_DISGUSTEDRIGHT_END,
+ PAT_SARCASTICLEFT_START,
+ PAT_SARCASTICLEFT_LOOP,
+ PAT_SARCASTICLEFT_END,
+ PAT_SARCASTICRIGHT_START,
+ PAT_SARCASTICRIGHT_LOOP,
+ PAT_SARCASTICRIGHT_END,
+
+ // Stand scared
+ PAT_SCAREDLEFT_STAND,
+ PAT_SCAREDRIGHT_STAND,
+ PAT_SCAREDDOWN_STAND,
+
+ PAT_PUTLEFT_UP1,
+ PAT_PUTLEFT_UP2,
+ PAT_PUTRIGHT_UP1,
+ PAT_PUTRIGHT_UP2,
+ PAT_PUTLEFT_MID1,
+ PAT_PUTLEFT_MID2,
+ PAT_PUTRIGHT_MID1,
+ PAT_PUTRIGHT_MID2,
+ PAT_PUTLEFT_DOWN1,
+ PAT_PUTLEFT_DOWN2,
+ PAT_PUTRIGHT_DOWN1,
+ PAT_PUTRIGHT_DOWN2,
+ PAT_PUTUP_UP1,
+ PAT_PUTUP_UP2,
+ PAT_PUTUP_MID1,
+ PAT_PUTUP_MID2,
+ PAT_PUTUP_DOWN1,
+ PAT_PUTUP_DOWN2,
+
+ PAT_WITHSECRETARY
+ };
+
+ enum CharacterBodyPatterns {
+ BPAT_STANDUP = 1,
+ BPAT_STANDDOWN,
+ BPAT_STANDLEFT,
+ BPAT_STANDRIGHT,
+
+ BPAT_HAMMER,
+ BPAT_SNOWMAN,
+ BPAT_WORM,
+ BPAT_GLASS,
+
+ BPAT_SINGLEFT_START,
+ BPAT_SINGLEFT_LOOP,
+ BPAT_SINGLEFT_END,
+
+ BPAT_HIPSLEFT_START,
+ BPAT_HIPSLEFT_LOOP,
+ BPAT_HIPSLEFT_END,
+ BPAT_HIPSRIGHT_START,
+ BPAT_HIPSRIGHT_LOOP,
+ BPAT_HIPSRIGHT_END,
+ BPAT_HIPSUP_START,
+ BPAT_HIPSUP_LOOP,
+ BPAT_HIPSUP_END,
+ BPAT_HIPSDOWN_START,
+ BPAT_HIPSDOWN_LOOP,
+ BPAT_HIPSDOWN_END,
+
+ BPAT_LAUGHLEFT,
+ BPAT_LAUGHRIGHT,
+
+ BPAT_INDICATELEFT,
+ BPAT_INDICATERIGHT,
+
+ BPAT_SCAREDDOWN_START,
+ BPAT_SCAREDDOWN_LOOP,
+ BPAT_SCAREDDOWN_END,
+ BPAT_SCAREDLEFT_START,
+ BPAT_SCAREDLEFT_LOOP,
+ BPAT_SCAREDLEFT_END,
+ BPAT_SCAREDRIGHT_START,
+ BPAT_SCAREDRIGHT_LOOP,
+ BPAT_SCAREDRIGHT_END,
+ BPAT_SCAREDUP_START,
+ BPAT_SCAREDUP_LOOP,
+ BPAT_SCAREDUP_END,
+
+ BPAT_ROPE,
+
+ BPAT_WITHRABBITLEFT_START,
+ BPAT_WITHRABBITLEFT_LOOP,
+ BPAT_WITHRABBITLEFT_END,
+ BPAT_WITHRABBITRIGHT_START,
+ BPAT_WITHRABBITRIGHT_LOOP,
+ BPAT_WITHRABBITRIGHT_END,
+
+ BPAT_WITHRECIPELEFT_START,
+ BPAT_WITHRECIPELEFT_LOOP,
+ BPAT_WITHRECIPELEFT_END,
+ BPAT_WITHRECIPERIGHT_START,
+ BPAT_WITHRECIPERIGHT_LOOP,
+ BPAT_WITHRECIPERIGHT_END,
+
+ BPAT_WITHCARDSLEFT_START,
+ BPAT_WITHCARDSLEFT_LOOP,
+ BPAT_WITHCARDSLEFT_END,
+ BPAT_WITHCARDSRIGHT_START,
+ BPAT_WITHCARDSRIGHT_LOOP,
+ BPAT_WITHCARDSRIGHT_END,
+
+ BPAT_WITHSNOWMANLEFT_START,
+ BPAT_WITHSNOWMANLEFT_LOOP,
+ BPAT_WITHSNOWMANLEFT_END,
+ BPAT_WITHSNOWMANRIGHT_START,
+ BPAT_WITHSNOWMANRIGHT_LOOP,
+ BPAT_WITHSNOWMANRIGHT_END,
+
+ BPAT_WITHNOTEBOOKLEFT_START,
+ BPAT_WITHNOTEBOOKLEFT_LOOP,
+ BPAT_WITHNOTEBOOKLEFT_END,
+ BPAT_WITHNOTEBOOKRIGHT_START,
+ BPAT_WITHNOTEBOOKRIGHT_LOOP,
+ BPAT_WITHNOTEBOOKRIGHT_END,
+
+ BPAT_WITHMEGAPHONELEFT_START,
+ BPAT_WITHMEGAPHONELEFT_LOOP,
+ BPAT_WITHMEGAPHONELEFT_END,
+ BPAT_WITHMEGAPHONERIGHT_START,
+ BPAT_WITHMEGAPHONERIGHT_LOOP,
+ BPAT_WITHMEGAPHONERIGHT_END,
+
+ BPAT_WITHBEARDLEFT_START,
+ BPAT_WITHBEARDLEFT_END,
+ BPAT_WITHBEARDRIGHT_START,
+ BPAT_WITHBEARDRIGHT_END,
+ BPAT_WITHBEARDLEFT_STATIC,
+ BPAT_WITHBEARDRIGHT_STATIC,
+
+ BPAT_MACBETH1,
+ BPAT_MACBETH2,
+ BPAT_MACBETH3,
+ BPAT_MACBETH4,
+ BPAT_MACBETH5,
+ BPAT_MACBETH6,
+ BPAT_MACBETH7,
+ BPAT_MACBETH8,
+ BPAT_MACBETH9,
+
+ BPAT_WITHSECRETARY
+ };
+
+public:
+ static void initStatics();
+ RMTony();
+
+ /**
+ * Initialize Tony
+ */
+ void init();
+
+ /**
+ * Free all memory
+ */
+ void close();
+
+ /**
+ * Tony makes a frame, updating the movement, etc.
+ */
+ void doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int curLoc);
+
+ /**
+ * Draw method, which controls chararacter display
+ */
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ /**
+ * Show or hide
+ */
+ void show();
+ void hide(bool bShowShadow = false);
+
+ /**
+ * Move and make an action, if necessary
+ */
+ void moveAndDoAction(CORO_PARAM, RMPoint dst, RMItem *item, int nAction, int nActionParm = 0);
+
+ /**
+ * Tony stops (on the right side with respect to any subject)
+ */
+ virtual void stop(CORO_PARAM);
+ void stopNoAction(CORO_PARAM);
+
+ /**
+ * Set a pattern
+ */
+ void setPattern(int npatt, bool bPlayP0 = false);
+
+ /**
+ * Reads the current pattern
+ */
+ int getCurPattern();
+
+ /**
+ * Waits until the end of a pattern
+ */
+ void waitForEndPattern(CORO_PARAM, uint32 hCustomSkip = CORO_INVALID_PID_VALUE);
+
+ /**
+ * Check if currently in an action
+ */
+ bool inAction();
+
+ /**
+ * Check if there needs to be an update for scrolling movement
+ */
+ bool mustUpdateScrolling();
+
+ /**
+ * Returns Tony's position
+ */
+ RMPoint position();
+
+ /**
+ * Set the scrolling position
+ */
+ void setScrollPosition(const RMPoint &pt);
+
+ /**
+ * Set the take animation
+ */
+ void take(int nWhere, int nPart);
+ void put(int nWhere, int nPart);
+
+ /**
+ * Start or End Talk
+ */
+ bool startTalkCalculate(CharacterTalkType nTalkType, int &headStartPat, int &bodyStartPat,
+ int &headLoopPat, int &bodyLoopPat);
+ void startTalk(CORO_PARAM, CharacterTalkType nTalkType);
+ bool endTalkCalculate(int &headStandPat, int &headEndPat, int &bodyEndPat, int &finalPat, bool &bStatic);
+ void endTalk(CORO_PARAM);
+
+ /**
+ * Start or End Static
+ */
+ void startStaticCalculate(CharacterTalkType nTalk, int &headPat, int &headLoopPat,
+ int &bodyStartPat, int &bodyLoopPat);
+ void startStatic(CORO_PARAM, CharacterTalkType nTalkType);
+ void endStaticCalculate(CharacterTalkType nTalk, int &bodyEndPat, int &finalPat, int &headEndPat);
+ void endStatic(CORO_PARAM, CharacterTalkType nTalkType);
+
+ /**
+ * Tony disguises himself!
+ */
+ void setShepherdess(bool bIsPast);
+
+ int getShepherdess();
+
+ /**
+ * Perform an action
+ */
+ void executeAction(int nAction, int nActionItem, int nParm);
+
+ void playSfx(int nSfx);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/utils.cpp b/engines/tony/utils.cpp
new file mode 100644
index 0000000000..81060146b7
--- /dev/null
+++ b/engines/tony/utils.cpp
@@ -0,0 +1,448 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/utils.h"
+#include "tony/tony.h"
+#include "tony/mpal/lzo.h"
+
+namespace Tony {
+
+/**
+ * Extracts a string from a data stream
+ * @param df data stream
+ */
+Common::String readString(Common::ReadStream &df) {
+ Common::String var;
+ uint8 len = df.readByte();
+
+ for (int i = 0; i < len; i++) {
+ char c;
+ c = df.readByte();
+ var += c;
+ }
+
+ return var;
+}
+
+/****************************************************************************\
+* RMPoint methods
+\****************************************************************************/
+
+/**
+ * Constructor
+ */
+RMPoint::RMPoint() {
+ _x = _y = 0;
+}
+
+/**
+ * Copy constructor
+ */
+RMPoint::RMPoint(const RMPoint &p) {
+ _x = p._x;
+ _y = p._y;
+}
+
+/**
+ * Constructor with integer parameters
+ */
+RMPoint::RMPoint(int x1, int y1) {
+ _x = x1;
+ _y = y1;
+}
+
+/**
+ * Copy operator
+ */
+RMPoint &RMPoint::operator=(RMPoint p) {
+ _x = p._x;
+ _y = p._y;
+
+ return *this;
+}
+
+/**
+ * Set a point
+ */
+void RMPoint::set(int x1, int y1) {
+ _x = x1;
+ _y = y1;
+}
+
+/**
+ * Offsets the point by another point
+ */
+void RMPoint::offset(const RMPoint &p) {
+ _x += p._x;
+ _y += p._y;
+}
+
+/**
+ * Offsets the point by a specified offset
+ */
+void RMPoint::offset(int xOff, int yOff) {
+ _x += xOff;
+ _y += yOff;
+}
+
+/**
+ * Sums together two points
+ */
+RMPoint operator+(RMPoint p1, RMPoint p2) {
+ RMPoint p(p1);
+
+ return (p += p2);
+}
+
+/**
+ * Subtracts two points
+ */
+RMPoint operator-(RMPoint p1, RMPoint p2) {
+ RMPoint p(p1);
+
+ return (p -= p2);
+}
+
+/**
+ * Sum (offset) of a point
+ */
+RMPoint &RMPoint::operator+=(RMPoint p) {
+ offset(p);
+ return *this;
+}
+
+/**
+ * Subtract (offset) of a point
+ */
+RMPoint &RMPoint::operator-=(RMPoint p) {
+ offset(-p);
+ return *this;
+}
+
+/**
+ * Inverts a point
+ */
+RMPoint RMPoint::operator-() {
+ RMPoint p;
+
+ p._x = -_x;
+ p._y = -_y;
+
+ return p;
+}
+
+/**
+ * Equality operator
+ */
+bool RMPoint::operator==(RMPoint p) {
+ return ((_x == p._x) && (_y == p._y));
+}
+
+/**
+ * Not equal operator
+ */
+bool RMPoint::operator!=(RMPoint p) {
+ return ((_x != p._x) || (_y != p._y));
+}
+
+/**
+ * Reads a point from a stream
+ */
+void RMPoint::readFromStream(Common::ReadStream &ds) {
+ _x = ds.readSint32LE();
+ _y = ds.readSint32LE();
+}
+
+/****************************************************************************\
+* RMPointReference methods
+\****************************************************************************/
+
+RMPointReference::RMPointReference(int &x, int &y): _x(x), _y(y) {
+}
+
+RMPointReference &RMPointReference::operator=(const RMPoint &p) {
+ _x = p._x; _y = p._y;
+ return *this;
+}
+
+RMPointReference &RMPointReference::operator-=(const RMPoint &p) {
+ _x -= p._x; _y -= p._y;
+ return *this;
+}
+
+RMPointReference::operator RMPoint() const {
+ return RMPoint(_x, _y);
+}
+
+/****************************************************************************\
+* RMRect methods
+\****************************************************************************/
+
+RMRect::RMRect(): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
+ setEmpty();
+}
+
+void RMRect::setEmpty() {
+ _x1 = _y1 = _x2 = _y2 = 0;
+}
+
+RMRect::RMRect(const RMPoint &p1, const RMPoint &p2): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
+ setRect(p1, p2);
+}
+
+RMRect::RMRect(int X1, int Y1, int X2, int Y2): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
+ setRect(X1, Y1, X2, Y2);
+}
+
+RMRect::RMRect(const RMRect &rc): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
+ copyRect(rc);
+}
+
+void RMRect::setRect(const RMPoint &p1, const RMPoint &p2) {
+ _x1 = p1._x;
+ _y1 = p1._y;
+ _x2 = p2._x;
+ _y2 = p2._y;
+}
+
+void RMRect::setRect(int X1, int Y1, int X2, int Y2) {
+ _x1 = X1;
+ _y1 = Y1;
+ _x2 = X2;
+ _y2 = Y2;
+}
+
+void RMRect::setRect(const RMRect &rc) {
+ copyRect(rc);
+}
+
+void RMRect::copyRect(const RMRect &rc) {
+ _x1 = rc._x1;
+ _y1 = rc._y1;
+ _x2 = rc._x2;
+ _y2 = rc._y2;
+}
+
+RMPointReference &RMRect::topLeft() {
+ return _topLeft;
+}
+
+RMPointReference &RMRect::bottomRight() {
+ return _bottomRight;
+}
+
+RMPoint RMRect::center() {
+ return RMPoint((_x2 - _x1) / 2, (_y2 - _y1) / 2);
+}
+
+int RMRect::width() const {
+ return _x2 - _x1;
+}
+
+int RMRect::height() const {
+ return _y2 - _y1;
+}
+
+int RMRect::size() const {
+ return width() * height();
+}
+
+RMRect::operator Common::Rect() const {
+ return Common::Rect(_x1, _y1, _x2, _y2);
+}
+
+bool RMRect::isEmpty() const {
+ return (_x1 == 0 && _y1 == 0 && _x2 == 0 && _y2 == 0);
+}
+
+const RMRect &RMRect::operator=(const RMRect &rc) {
+ copyRect(rc);
+ return *this;
+}
+
+void RMRect::offset(int xOff, int yOff) {
+ _x1 += xOff;
+ _y1 += yOff;
+ _x2 += xOff;
+ _y2 += yOff;
+}
+
+void RMRect::offset(const RMPoint &p) {
+ _x1 += p._x;
+ _y1 += p._y;
+ _x2 += p._x;
+ _y2 += p._y;
+}
+
+const RMRect &RMRect::operator+=(RMPoint p) {
+ offset(p);
+ return *this;
+}
+
+const RMRect &RMRect::operator-=(RMPoint p) {
+ offset(-p);
+ return *this;
+}
+
+RMRect operator+(const RMRect &rc, RMPoint p) {
+ RMRect r(rc);
+ return (r += p);
+}
+
+RMRect operator-(const RMRect &rc, RMPoint p) {
+ RMRect r(rc);
+
+ return (r -= p);
+}
+
+RMRect operator+(RMPoint p, const RMRect &rc) {
+ RMRect r(rc);
+
+ return (r += p);
+}
+
+RMRect operator-(RMPoint p, const RMRect &rc) {
+ RMRect r(rc);
+
+ return (r += p);
+}
+
+bool RMRect::operator==(const RMRect &rc) {
+ return ((_x1 == rc._x1) && (_y1 == rc._y1) && (_x2 == rc._x2) && (_y2 == rc._y2));
+}
+
+bool RMRect::operator!=(const RMRect &rc) {
+ return ((_x1 != rc._x1) || (_y1 != rc._y1) || (_x2 != rc._x2) || (_y2 != rc._y2));
+}
+
+void RMRect::normalizeRect() {
+ setRect(MIN(_x1, _x2), MIN(_y1, _y2), MAX(_x1, _x2), MAX(_y1, _y2));
+}
+
+void RMRect::readFromStream(Common::ReadStream &ds) {
+ _x1 = ds.readSint32LE();
+ _y1 = ds.readSint32LE();
+ _x2 = ds.readSint32LE();
+ _y2 = ds.readSint32LE();
+}
+
+/**
+ * Check if RMPoint is in RMRect
+ */
+bool RMRect::ptInRect(const RMPoint &pt) {
+ return (pt._x >= _x1 && pt._x <= _x2 && pt._y >= _y1 && pt._y <= _y2);
+}
+
+/****************************************************************************\
+* Resource Update
+\****************************************************************************/
+
+RMResUpdate::RMResUpdate() {
+ _infos = NULL;
+ _numUpd = 0;
+}
+
+RMResUpdate::~RMResUpdate() {
+ if (_infos) {
+ delete[] _infos;
+ _infos = NULL;
+ }
+
+ if (_hFile.isOpen())
+ _hFile.close();
+}
+
+void RMResUpdate::init(const Common::String &fileName) {
+ // Open the resource update file
+ if (!_hFile.open(fileName))
+ // It doesn't exist, so exit immediately
+ return;
+
+ _hFile.readByte(); // Version, unused
+
+ _numUpd = _hFile.readUint32LE();
+
+ _infos = new ResUpdInfo[_numUpd];
+
+ // Load the index of the resources in the file
+ for (uint32 i = 0; i < _numUpd; ++i) {
+ ResUpdInfo &info = _infos[i];
+
+ info._dwRes = _hFile.readUint32LE();
+ info._offset = _hFile.readUint32LE();
+ info._size = _hFile.readUint32LE();
+ info._cmpSize = _hFile.readUint32LE();
+ }
+}
+
+MpalHandle RMResUpdate::queryResource(uint32 dwRes) {
+ // If there isn't an update file, return NULL
+ if (!_hFile.isOpen())
+ return NULL;
+
+ uint32 i;
+ for (i = 0; i < _numUpd; ++i)
+ if (_infos[i]._dwRes == dwRes)
+ // Found the index
+ break;
+
+ if (i == _numUpd)
+ // Couldn't find a matching resource, so return NULL
+ return NULL;
+
+ const ResUpdInfo &info = _infos[i];
+ byte *cmpBuf = new byte[info._cmpSize];
+ uint32 dwRead;
+
+ // Move to the correct offset and read in the compressed data
+ _hFile.seek(info._offset);
+ dwRead = _hFile.read(cmpBuf, info._cmpSize);
+
+ if (info._cmpSize > dwRead) {
+ // Error occurred reading data, so return NULL
+ delete[] cmpBuf;
+ return NULL;
+ }
+
+ // Allocate space for the output resource
+ MpalHandle destBuf = globalAllocate(0, info._size);
+ byte *lpDestBuf = (byte *)globalLock(destBuf);
+ uint32 dwSize;
+
+ // Decompress the data
+ lzo1x_decompress(cmpBuf, info._cmpSize, lpDestBuf, &dwSize);
+
+ // Delete buffer for compressed data
+ delete [] cmpBuf;
+
+ // Return the resource
+ globalUnlock(destBuf);
+ return destBuf;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/utils.h b/engines/tony/utils.h
new file mode 100644
index 0000000000..9f13e5f19b
--- /dev/null
+++ b/engines/tony/utils.h
@@ -0,0 +1,176 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_UTILS_H
+#define TONY_UTILS_H
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+using namespace ::Tony::MPAL;
+
+Common::String readString(Common::ReadStream &ds);
+
+/**
+ * Point class
+ */
+class RMPoint {
+public:
+ int _x, _y;
+
+public:
+ // Constructor
+ RMPoint();
+ RMPoint(const RMPoint &p);
+ RMPoint(int x1, int y1);
+
+ // Copy
+ RMPoint &operator=(RMPoint p);
+
+ // Set
+ void set(int x1, int y1);
+
+ // Offset
+ void offset(int xOff, int yOff);
+ void offset(const RMPoint &p);
+ friend RMPoint operator+(RMPoint p1, RMPoint p2);
+ friend RMPoint operator-(RMPoint p1, RMPoint p2);
+ RMPoint &operator+=(RMPoint p);
+ RMPoint &operator-=(RMPoint p);
+ RMPoint operator-();
+
+ // Comparison
+ bool operator==(RMPoint p);
+ bool operator!=(RMPoint p);
+
+ // Casting a POINT
+ operator Common::Point() const;
+
+ // Extraction from data streams
+ void readFromStream(Common::ReadStream &ds);
+};
+
+class RMPointReference {
+public:
+ int &_x;
+ int &_y;
+
+ RMPointReference(int &x, int &y);
+ RMPointReference &operator=(const RMPoint &p);
+ RMPointReference &operator-=(const RMPoint &p);
+ operator RMPoint() const;
+};
+
+class RMRect {
+public:
+ int _x1, _y1;
+ int _x2, _y2;
+ RMPointReference _topLeft;
+ RMPointReference _bottomRight;
+
+public:
+ RMRect();
+ RMRect(int x1, int y1, int x2, int y2);
+ RMRect(const RMPoint &p1, const RMPoint &p2);
+ RMRect(const RMRect &rc);
+
+ // Attributes
+ RMPointReference &topLeft();
+ RMPointReference &bottomRight();
+ RMPoint center();
+ int width() const;
+ int height() const;
+ bool isEmpty() const;
+ int size() const;
+ operator Common::Rect() const;
+
+ // Set
+ void setRect(int x1, int y1, int x2, int y2);
+ void setRect(const RMPoint &p1, const RMPoint &p2);
+ void setEmpty();
+
+ // Copiers
+ void setRect(const RMRect &rc);
+ void copyRect(const RMRect &rc);
+ const RMRect &operator=(const RMRect &rc);
+
+ // Offset
+ void offset(int xOff, int yOff);
+ void offset(const RMPoint &p);
+ friend RMRect operator+(const RMRect &rc, RMPoint p);
+ friend RMRect operator-(const RMRect &rc, RMPoint p);
+ friend RMRect operator+(RMPoint p, const RMRect &rc);
+ friend RMRect operator-(RMPoint p, const RMRect &rc);
+ const RMRect &operator+=(RMPoint p);
+ const RMRect &operator-=(RMPoint p);
+
+ // Comparison
+ bool operator==(const RMRect &rc);
+ bool operator!=(const RMRect &rc);
+
+ // Normalize
+ void normalizeRect();
+
+ // Point in rect
+ bool ptInRect(const RMPoint &pt);
+
+ // Extract from data stream
+ void readFromStream(Common::ReadStream &ds);
+};
+
+/**
+ * Resource update manager
+ */
+class RMResUpdate {
+ struct ResUpdInfo {
+ uint32 _dwRes;
+ uint32 _offset;
+ uint32 _size;
+ uint32 _cmpSize;
+ };
+
+ uint32 _numUpd;
+ ResUpdInfo *_infos;
+ Common::File _hFile;
+
+public:
+ RMResUpdate();
+ ~RMResUpdate();
+
+ void init(const Common::String &fileName);
+ MpalHandle queryResource(uint32 dwRes);
+};
+
+} // End of namespace Tony
+
+#endif /* TONY_H */
diff --git a/engines/tony/window.cpp b/engines/tony/window.cpp
new file mode 100644
index 0000000000..c9c450424f
--- /dev/null
+++ b/engines/tony/window.cpp
@@ -0,0 +1,336 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/scummsys.h"
+#include "graphics/surface.h"
+#include "util.h"
+#include "tony/window.h"
+#include "tony/game.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+
+/****************************************************************************\
+* RMWindow Methods
+\****************************************************************************/
+
+RMWindow::RMWindow() {
+ _showDirtyRects = false;
+}
+
+RMWindow::~RMWindow() {
+ close();
+ RMText::unload();
+ RMGfxTargetBuffer::freeBWPrecalcTable();
+}
+
+/**
+ * Initializes the graphics window
+ */
+void RMWindow::init() {
+ Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ initGraphics(RM_SX, RM_SY, true, &pixelFormat);
+
+ _bGrabScreenshot = false;
+ _bGrabThumbnail = false;
+ _bGrabMovie = false;
+ _wiping = false;
+}
+
+void RMWindow::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
+ if (GLOBALS._bCfgAnni30) {
+ if (!RMGfxTargetBuffer::_precalcTable) {
+ RMGfxTargetBuffer::createBWPrecalcTable();
+ g_vm->getEngine()->getPointer().updateCursor();
+ }
+ Graphics::Surface *screen = g_system->lockScreen();
+ const uint16 *src = (const uint16 *)buf;
+ for (int i = 0; i < h; i++) {
+ uint16 *dst = (uint16 *)screen->getBasePtr(x, y + i);
+ for (int j = 0; j < w; j++) {
+ dst[j] = RMGfxTargetBuffer::_precalcTable[src[j] & 0x7FFF];
+ }
+ src += (pitch / 2);
+ }
+ g_system->unlockScreen();
+ } else {
+ if (RMGfxTargetBuffer::_precalcTable) {
+ RMGfxTargetBuffer::freeBWPrecalcTable();
+ g_vm->getEngine()->getPointer().updateCursor();
+ }
+ g_system->copyRectToScreen(buf, pitch, x, y, w, h);
+ }
+ }
+
+/**
+ * Close the window
+ */
+void RMWindow::close() {
+}
+
+void RMWindow::grabThumbnail(uint16 *thumbmem) {
+ _bGrabThumbnail = true;
+ _wThumbBuf = thumbmem;
+}
+
+/**
+ * Repaint the screen
+ */
+void RMWindow::repaint() {
+ g_system->updateScreen();
+}
+
+/**
+ * Wipes an area of the screen
+ */
+void RMWindow::wipeEffect(Common::Rect &rcBoundEllipse) {
+ if ((rcBoundEllipse.left == 0) && (rcBoundEllipse.top == 0) &&
+ (rcBoundEllipse.right == RM_SX) && (rcBoundEllipse.bottom == RM_SY)) {
+ // Full screen clear wanted, so use shortcut method
+ g_system->fillScreen(0);
+ } else {
+ // Clear the designated area a line at a time
+ uint16 line[RM_SX];
+ Common::fill(line, line + RM_SX, 0);
+
+ // Loop through each line
+ for (int yp = rcBoundEllipse.top; yp < rcBoundEllipse.bottom; ++yp) {
+ copyRectToScreen((const byte *)&line[0], RM_SX * 2, rcBoundEllipse.left, yp, rcBoundEllipse.width(), 1);
+ }
+ }
+}
+
+void RMWindow::getNewFrame(RMGfxTargetBuffer &bigBuf, Common::Rect *rcBoundEllipse) {
+ // Get a pointer to the bytes of the source buffer
+ byte *lpBuf = bigBuf;
+
+ if (rcBoundEllipse != NULL) {
+ // Circular wipe effect
+ getNewFrameWipe(lpBuf, *rcBoundEllipse);
+ _wiping = true;
+ } else if (_wiping) {
+ // Just finished a wiping effect, so copy the full screen
+ copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
+ _wiping = false;
+
+ } else {
+ // Standard screen copy - iterate through the dirty rects
+ Common::List<Common::Rect> dirtyRects = bigBuf.getDirtyRects();
+ Common::List<Common::Rect>::iterator i;
+
+ // If showing dirty rects, copy the entire screen background and set up a surface pointer
+ Graphics::Surface *s = NULL;
+ if (_showDirtyRects) {
+ copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
+ s = g_system->lockScreen();
+ }
+
+ for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) {
+ Common::Rect &r = *i;
+ const byte *lpSrc = lpBuf + (RM_SX * 2) * r.top + (r.left * 2);
+ copyRectToScreen(lpSrc, RM_SX * 2, r.left, r.top, r.width(), r.height());
+ }
+
+ if (_showDirtyRects) {
+ for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) {
+ // Frame the copied area with a rectangle
+ s->frameRect(*i, 0xffffff);
+ }
+
+ g_system->unlockScreen();
+ }
+ }
+
+ if (_bGrabThumbnail) {
+ // Need to generate a thumbnail
+ RMSnapshot s;
+
+ s.grabScreenshot(lpBuf, 4, _wThumbBuf);
+ _bGrabThumbnail = false;
+ }
+
+ // Clear the dirty rect list
+ bigBuf.clearDirtyRects();
+}
+
+/**
+ * Copies a section of the game frame in a circle bounded by the specified rectangle
+ */
+void RMWindow::getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse) {
+ // Clear the screen
+ g_system->fillScreen(0);
+
+ if (!rcBoundEllipse.isValidRect())
+ return;
+
+ Common::Point center(rcBoundEllipse.left + rcBoundEllipse.width() / 2,
+ rcBoundEllipse.top + rcBoundEllipse.height() / 2);
+
+ // The rectangle technically defines the area inside the ellipse, with the corners touching
+ // the ellipse boundary. Since we're currently simulating the ellipse using a plain circle,
+ // we need to calculate a necessary width using the hypotenuse of X/2 & Y/2
+ int x2y2 = (rcBoundEllipse.width() / 2) * (rcBoundEllipse.width() / 2) +
+ (rcBoundEllipse.height() / 2) * (rcBoundEllipse.height() / 2);
+ int radius = 0;
+ while ((radius * radius) < x2y2)
+ ++radius;
+
+ // Proceed copying a circular area of the frame with the calculated radius onto the screen
+ int error = -radius;
+ int x = radius;
+ int y = 0;
+
+ while (x >= y) {
+ plotSplices(lpBuf, center, x, y);
+
+ error += y;
+ ++y;
+ error += y;
+
+ if (error >= 0) {
+ error -= x;
+ --x;
+ error -= x;
+ }
+ }
+}
+
+/**
+ * Handles drawing the line splices for the circle of viewable area
+ */
+void RMWindow::plotSplices(const byte *lpBuf, const Common::Point &center, int x, int y) {
+ plotLines(lpBuf, center, x, y);
+ if (x != y)
+ plotLines(lpBuf, center, y, x);
+}
+
+/**
+ * Handles drawing the line splices for the circle of viewable area
+ */
+void RMWindow::plotLines(const byte *lpBuf, const Common::Point &center, int x, int y) {
+ // Skips lines that have no width (i.e. at the top of the circle)
+ if ((x == 0) || (y > center.y))
+ return;
+
+ const byte *pSrc;
+ int xs = MAX(center.x - x, 0);
+ int width = MIN(RM_SX - xs, x * 2);
+
+ if ((center.y - y) >= 0) {
+ // Draw line in top half of circle
+ pSrc = lpBuf + ((center.y - y) * RM_SX * 2) + xs * 2;
+ copyRectToScreen(pSrc, RM_SX * 2, xs, center.y - y, width, 1);
+ }
+
+ if ((center.y + y) < RM_SY) {
+ // Draw line in bottom half of circle
+ pSrc = lpBuf + ((center.y + y) * RM_SX * 2) + xs * 2;
+ copyRectToScreen(pSrc, RM_SX * 2, xs, center.y + y, width, 1);
+ }
+}
+
+void RMWindow::showDirtyRects(bool v) {
+ _showDirtyRects = v;
+}
+
+/****************************************************************************\
+* RMSnapshot Methods
+\****************************************************************************/
+
+void RMSnapshot::grabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) {
+ uint16 *src = (uint16 *)lpBuf;
+
+ int dimx = RM_SX / dezoom;
+ int dimy = RM_SY / dezoom;
+
+ uint16 *cursrc;
+
+ if (lpDestBuf == NULL)
+ src += (RM_SY - 1) * RM_BBX;
+
+ if (dezoom == 1 && 0) {
+ byte *curOut = _rgb;
+
+ for (int y = 0; y < dimy; y++) {
+ for (int x = 0; x < dimx; x++) {
+ cursrc = &src[RM_SKIPX + x];
+
+ *curOut++ = ((*cursrc) & 0x1F) << 3;
+ *curOut++ = (((*cursrc) >> 5) & 0x1F) << 3;
+ *curOut++ = (((*cursrc) >> 10) & 0x1F) << 3;
+
+ if (lpDestBuf)
+ *lpDestBuf++ = *cursrc;
+ }
+
+ if (lpDestBuf == NULL)
+ src -= RM_BBX;
+ else
+ src += RM_BBX;
+ }
+ } else {
+ uint32 k = 0;
+ for (int y = 0; y < dimy; y++) {
+ for (int x = 0; x < dimx; x++) {
+ cursrc = &src[RM_SKIPX + x * dezoom];
+ int sommar, sommab, sommag, curv;
+ sommar = sommab = sommag = 0;
+
+ for (int v = 0; v < dezoom; v++) {
+ for (int u = 0; u < dezoom; u++) {
+ if (lpDestBuf == NULL)
+ curv = -v;
+ else
+ curv = v;
+
+ sommab += cursrc[curv * RM_BBX + u] & 0x1F;
+ sommag += (cursrc[curv * RM_BBX + u] >> 5) & 0x1F;
+ sommar += (cursrc[curv * RM_BBX + u] >> 10) & 0x1F;
+ }
+ }
+ _rgb[k + 0] = (byte)(sommab * 8 / (dezoom * dezoom));
+ _rgb[k + 1] = (byte)(sommag * 8 / (dezoom * dezoom));
+ _rgb[k + 2] = (byte)(sommar * 8 / (dezoom * dezoom));
+
+ if (lpDestBuf != NULL)
+ lpDestBuf[k / 3] = ((int)_rgb[k + 0] >> 3) | (((int)_rgb[k + 1] >> 3) << 5) |
+ (((int)_rgb[k + 2] >> 3) << 10);
+
+ k += 3;
+ }
+
+ if (lpDestBuf == NULL)
+ src -= RM_BBX * dezoom;
+ else
+ src += RM_BBX * dezoom;
+ }
+ }
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/window.h b/engines/tony/window.h
new file mode 100644
index 0000000000..2e8769707f
--- /dev/null
+++ b/engines/tony/window.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_WINDOW_H
+#define TONY_WINDOW_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "tony/game.h"
+
+namespace Tony {
+
+class RMSnapshot {
+private:
+ // Buffer used to convert to RGB
+ byte _rgb[RM_SX *RM_SY * 3];
+public:
+ /**
+ * Take a screenshot
+ */
+ void grabScreenshot(byte *lpBuf, int dezoom = 1, uint16 *lpDestBuf = NULL);
+};
+
+
+class RMWindow {
+private:
+ void plotSplices(const byte *lpBuf, const Common::Point &center, int x, int y);
+ void plotLines(const byte *lpBuf, const Common::Point &center, int x, int y);
+
+protected:
+ bool _wiping;
+ bool _showDirtyRects;
+
+ bool _bGrabScreenshot;
+ bool _bGrabThumbnail;
+ bool _bGrabMovie;
+ uint16 *_wThumbBuf;
+
+ void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h);
+ void wipeEffect(Common::Rect &rcBoundEllipse);
+ void getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse);
+
+public:
+ RMWindow();
+ ~RMWindow();
+
+ /**
+ * Initialization
+ */
+ void init();
+ void close();
+
+ /**
+ * Drawing
+ */
+ void repaint();
+
+ /**
+ * Reads the next frame
+ */
+ void getNewFrame(RMGfxTargetBuffer &lpBuf, Common::Rect *rcBoundEllipse);
+
+ /**
+ * Request a thumbnail be grabbed during the next frame
+ */
+ void grabThumbnail(uint16 *buf);
+
+ void showDirtyRects(bool v);
+};
+
+} // End of namespace Tony
+
+#endif /* TONY_WINDOW_H */
diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp
index 3877fa2a6c..38b1f4f6e1 100644
--- a/engines/toon/detection.cpp
+++ b/engines/toon/detection.cpp
@@ -84,7 +84,7 @@ static const ADGameDescription gameDescriptions[] = {
{"study.svl", 0, "d4aff126ee27be3c3d25e2996369d7cb", 2324368},
},
Common::RU_RUS, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO0()
- },
+ },
{
"toon", "",
{
diff --git a/engines/toon/movie.h b/engines/toon/movie.h
index e795182cba..4dd6583bf6 100644
--- a/engines/toon/movie.h
+++ b/engines/toon/movie.h
@@ -40,7 +40,7 @@ protected:
SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const;
private:
- bool _lowRes;
+ bool _lowRes;
};
class Movie {
diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp
index 204b0fe576..f59cdca064 100644
--- a/engines/toon/picture.cpp
+++ b/engines/toon/picture.cpp
@@ -71,7 +71,7 @@ bool Picture::loadPicture(const Common::String &file) {
_data = new uint8[decSize + 100];
_paletteEntries = READ_LE_UINT16(fileData + 14) / 3;
_useFullPalette = (_paletteEntries == 256);
-
+
if (_paletteEntries) {
_palette = new uint8[_paletteEntries * 3];
memcpy(_palette, fileData + 16, _paletteEntries * 3);
diff --git a/engines/touche/staticres.cpp b/engines/touche/staticres.cpp
index c18a947358..23b76558e4 100644
--- a/engines/touche/staticres.cpp
+++ b/engines/touche/staticres.cpp
@@ -471,7 +471,10 @@ const uint16 Graphics::_freGerFontOffs[] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x1920
+ 0x0000, 0x0000, 0x0000, 0x1920, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000
};
const int Graphics::_freGerFontSize = ARRAYSIZE(Graphics::_freGerFontOffs);
diff --git a/engines/tsage/blue_force/blueforce_scenes3.cpp b/engines/tsage/blue_force/blueforce_scenes3.cpp
index 22c831f531..81e4af6e97 100644
--- a/engines/tsage/blue_force/blueforce_scenes3.cpp
+++ b/engines/tsage/blue_force/blueforce_scenes3.cpp
@@ -346,6 +346,14 @@ void Scene300::postInit(SceneObjectList *OwnerList) {
break;
}
+ if (BF_GLOBALS.getFlag(onBike) && !BF_GLOBALS.getFlag(onDuty)) {
+ BF_GLOBALS._sound1.play(30);
+ } else if ((BF_GLOBALS._dayNumber == 2) && (BF_GLOBALS._bookmark < bEndDayOne)) {
+ BF_GLOBALS._sound1.changeSound(49);
+ } else if (BF_GLOBALS._sceneManager._previousScene != 190) {
+ BF_GLOBALS._sound1.changeSound(33);
+ }
+
_item10.setDetails(4, 300, 7, 13, 16, 1);
_item11.setDetails(2, 300, 9, 13, 18, 1);
_item12.setDetails(5, 300, 10, 13, 19, 1);
diff --git a/engines/tsage/blue_force/blueforce_scenes7.cpp b/engines/tsage/blue_force/blueforce_scenes7.cpp
index bb29ad1f34..4cdd2f3f15 100644
--- a/engines/tsage/blue_force/blueforce_scenes7.cpp
+++ b/engines/tsage/blue_force/blueforce_scenes7.cpp
@@ -159,7 +159,7 @@ void Scene710::postInit(SceneObjectList *OwnerList) {
_stripManager.addSpeaker(&_skipSpeaker);
_stripManager.addSpeaker(&_lauraSpeaker);
_stripManager.addSpeaker(&_gameTextSpeaker);
-
+
_kid.postInit();
_kid._moveDiff = Common::Point(4, 2);
_laura.postInit();
diff --git a/engines/tsage/blue_force/blueforce_speakers.cpp b/engines/tsage/blue_force/blueforce_speakers.cpp
index 8af18b43b8..2a57616640 100644
--- a/engines/tsage/blue_force/blueforce_speakers.cpp
+++ b/engines/tsage/blue_force/blueforce_speakers.cpp
@@ -809,7 +809,7 @@ void SpeakerGiggles::setText(const Common::String &msg) {
SpeakerFBI::SpeakerFBI(): VisualSpeaker() {
_color1 = 27;
_color2 = 89;
-
+
_speakerName = "FBI";
}
@@ -832,7 +832,7 @@ void SpeakerFBI::setText(const Common::String &msg) {
SpeakerNico::SpeakerNico(): VisualSpeaker() {
_color1 = 105;
_color2 = 102;
-
+
_speakerName = "NICO";
}
@@ -845,7 +845,7 @@ void SpeakerNico::setText(const Common::String &msg) {
_object1.fixPriority(254);
_object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 262,
BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166));
-
+
_object2.postInit();
_object2.setVisage(905);
_object2.setStrip2(1);
@@ -862,7 +862,7 @@ void SpeakerNico::setText(const Common::String &msg) {
SpeakerDA::SpeakerDA(): VisualSpeaker() {
_color1 = 82;
_color2 = 80;
-
+
_speakerName = "DA";
}
@@ -875,7 +875,7 @@ void SpeakerDA::setText(const Common::String &msg) {
_object1.fixPriority(254);
_object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 84,
BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166));
-
+
_object2.postInit();
_object2.setVisage(915);
_object2.setStrip2(1);
@@ -892,7 +892,7 @@ void SpeakerDA::setText(const Common::String &msg) {
SpeakerGrandma::SpeakerGrandma(): VisualSpeaker() {
_color1 = 20;
_color2 = 23;
-
+
_speakerName = "GRANDMA";
}
@@ -905,7 +905,7 @@ void SpeakerGrandma::setText(const Common::String &msg) {
_object1.fixPriority(254);
_object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 43,
BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166));
-
+
_object2.postInit();
_object2.setVisage(274);
_object2.setStrip2(3);
@@ -922,7 +922,7 @@ void SpeakerGrandma::setText(const Common::String &msg) {
SpeakerLyle::SpeakerLyle(): VisualSpeaker() {
_color1 = 29;
_color2 = 89;
-
+
_speakerName = "LYLE";
}
@@ -935,7 +935,7 @@ void SpeakerLyle::setText(const Common::String &msg) {
_object1.fixPriority(254);
_object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 75,
BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166));
-
+
_object2.postInit();
_object2.setVisage(278);
_object2.setStrip2(1);
@@ -952,7 +952,7 @@ void SpeakerLyle::setText(const Common::String &msg) {
SpeakerGranText::SpeakerGranText(): VisualSpeaker() {
_color1 = 20;
_color2 = 23;
-
+
_speakerName = "GRANTEXT";
}
@@ -961,7 +961,7 @@ SpeakerGranText::SpeakerGranText(): VisualSpeaker() {
SpeakerLyleText::SpeakerLyleText(): VisualSpeaker() {
_color1 = 29;
_color2 = 89;
-
+
_speakerName = "LYLETEXT";
}
@@ -969,7 +969,7 @@ SpeakerLyleText::SpeakerLyleText(): VisualSpeaker() {
SpeakerKate::SpeakerKate(): VisualSpeaker() {
_color1 = 108;
-
+
_speakerName = "KATE";
}
@@ -982,7 +982,7 @@ void SpeakerKate::setText(const Common::String &msg) {
_object1.fixPriority(254);
_object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 270,
BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166));
-
+
_object2.postInit();
_object2.setVisage(122);
_object2.setStrip2(1);
@@ -1000,7 +1000,7 @@ void SpeakerKate::setText(const Common::String &msg) {
SpeakerTony::SpeakerTony(): VisualSpeaker() {
_color1 = 108;
_color2 = 8;
-
+
_speakerName = "TONY";
}
diff --git a/engines/tsage/blue_force/blueforce_speakers.h b/engines/tsage/blue_force/blueforce_speakers.h
index 508279a929..e406a50fbe 100644
--- a/engines/tsage/blue_force/blueforce_speakers.h
+++ b/engines/tsage/blue_force/blueforce_speakers.h
@@ -290,7 +290,7 @@ public:
virtual Common::String getClassName() { return "FBI"; }
virtual void setText(const Common::String &msg);
};
-
+
class SpeakerNico: public VisualSpeaker {
public:
SpeakerNico();
@@ -340,7 +340,7 @@ public:
class SpeakerKate: public VisualSpeaker {
public:
SpeakerKate();
-
+
virtual Common::String getClassName() { return "SpeakerKate"; }
virtual void setText(const Common::String &msg);
};
@@ -348,7 +348,7 @@ public:
class SpeakerTony: public VisualSpeaker {
public:
SpeakerTony();
-
+
virtual Common::String getClassName() { return "SpeakerTony"; }
virtual void setText(const Common::String &msg);
};
diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp
index bcadfdc201..a35d663b93 100644
--- a/engines/tsage/detection.cpp
+++ b/engines/tsage/detection.cpp
@@ -156,7 +156,7 @@ public:
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const {
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(
generateGameStateFileName(target, slot));
-
+
if (f) {
TsAGE::tSageSavegameHeader header;
TsAGE::Saver::readSavegameHeader(f, header);
diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp
index de9463268b..4589a926c9 100644
--- a/engines/tsage/globals.cpp
+++ b/engines/tsage/globals.cpp
@@ -205,7 +205,7 @@ void Globals::dispatchSounds() {
void TsAGE2Globals::reset() {
Globals::reset();
-
+
// Reset the inventory
T2_GLOBALS._uiElements.updateInventory();
T2_GLOBALS._uiElements._scoreValue = 0;
@@ -277,7 +277,7 @@ void BlueForceGlobals::synchronize(Serializer &s) {
void BlueForceGlobals::reset() {
TsAGE2Globals::reset();
_scenePalette.clearListeners();
-
+
_scrollFollower = &_player;
_bookmark = bNone;
@@ -368,7 +368,7 @@ namespace Ringworld2 {
void Ringworld2Globals::reset() {
Globals::reset();
-
+
// Reset the inventory
R2_INVENTORY.reset();
T2_GLOBALS._uiElements.updateInventory();
@@ -526,7 +526,7 @@ void Ringworld2Globals::synchronize(Serializer &s) {
for (i = 0; i < MAX_CHARACTERS; ++i)
s.syncAsByte(_v565F1[i]);
-
+
s.syncAsByte(_v565AE);
s.syncAsByte(_v566A4);
s.syncAsByte(_v566A5);
diff --git a/engines/tsage/ringworld/ringworld_logic.cpp b/engines/tsage/ringworld/ringworld_logic.cpp
index 7d571b40d7..0584570ac2 100644
--- a/engines/tsage/ringworld/ringworld_logic.cpp
+++ b/engines/tsage/ringworld/ringworld_logic.cpp
@@ -610,7 +610,7 @@ void NamedHotspot::doAction(int action) {
case CURSOR_USE:
if (_useLineNum == -1)
break;
-
+
SceneItem::display(_resNum, _useLineNum, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END);
return;
case CURSOR_TALK:
diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp
index a06899fe5a..97042cb621 100644
--- a/engines/tsage/ringworld2/ringworld2_logic.cpp
+++ b/engines/tsage/ringworld2/ringworld2_logic.cpp
@@ -1695,7 +1695,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) {
_playbackTickPrior = -1;
_playbackTick = 0;
- // The final multiplication is used to deliberately slow down playback, since the original
+ // The final multiplication is used to deliberately slow down playback, since the original
// was slowed down by the amount of time spent to decode and display the frames
_frameDelay = (60 / _subData._frameRate) * 8;
_gameFrame = R2_GLOBALS._events.getFrameNumber();
@@ -1706,7 +1706,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) {
int v = (_subData._sliceSize + 2) * _subData._ySlices * _subData._framesPerSlices;
_dataNeeded = (_subData._field16 / _subData._framesPerSlices) + v + 96;
}
-
+
debugC(1, ktSageDebugGraphics, "Data needed %d", _dataNeeded);
// Set up animation data objects
@@ -1760,7 +1760,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) {
byte r = _subData._palData[idx * 3];
byte g = _subData._palData[idx * 3 + 1];
byte b = _subData._palData[idx * 3 + 2];
-
+
int palIndex = R2_GLOBALS._scenePalette.indexOf(r, g, b);
_palIndexes[idx] = palIndex;
}
diff --git a/engines/tsage/scenes.h b/engines/tsage/scenes.h
index 2daa71ba98..d5ac88c692 100644
--- a/engines/tsage/scenes.h
+++ b/engines/tsage/scenes.h
@@ -67,7 +67,7 @@ public:
void setZoomPercents(int yStart, int minPercent, int yEnd, int maxPercent);
void loadBackground(int xAmount, int yAmount);
-
+
void loadSceneData(int sceneNum);
};
diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp
index 9df5a6666b..69a9975ef4 100644
--- a/engines/tsage/sound.cpp
+++ b/engines/tsage/sound.cpp
@@ -66,7 +66,7 @@ SoundManager::~SoundManager() {
++i;
delete driver;
}
- _sfTerminate();
+ sfTerminate();
// g_system->getTimerManager()->removeTimerProc(_sfUpdateCallback);
}
@@ -132,7 +132,7 @@ void SoundManager::syncSounds() {
}
void SoundManager::update() {
- _sfSoundServer();
+ sfSoundServer();
}
Common::List<SoundDriverEntry> &SoundManager::buildDriverList(bool detectFlag) {
@@ -144,22 +144,22 @@ Common::List<SoundDriverEntry> &SoundManager::buildDriverList(bool detectFlag) {
// Adlib driver
SoundDriverEntry sd;
- sd.driverNum = ADLIB_DRIVER_NUM;
- sd.status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED;
- sd.field2 = 0;
- sd.field6 = 15000;
- sd.shortDescription = "Adlib or SoundBlaster";
- sd.longDescription = "3812fm";
+ sd._driverNum = ADLIB_DRIVER_NUM;
+ sd._status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED;
+ sd._field2 = 0;
+ sd._field6 = 15000;
+ sd._shortDescription = "Adlib or SoundBlaster";
+ sd._longDescription = "3812fm";
_availableDrivers.push_back(sd);
// SoundBlaster entry
SoundDriverEntry sdFx;
- sdFx.driverNum = SBLASTER_DRIVER_NUM;
- sdFx.status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED;
- sdFx.field2 = 0;
- sdFx.field6 = 15000;
- sdFx.shortDescription = "SndBlast";
- sdFx.longDescription = "SoundBlaster";
+ sdFx._driverNum = SBLASTER_DRIVER_NUM;
+ sdFx._status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED;
+ sdFx._field2 = 0;
+ sdFx._field6 = 15000;
+ sdFx._shortDescription = "SndBlast";
+ sdFx._longDescription = "SoundBlaster";
_availableDrivers.push_back(sdFx);
_driversDetected = true;
@@ -204,7 +204,7 @@ void SoundManager::installDriver(int driverNum) {
(*i)->mute(true);
// Install the driver
- if (!_sfInstallDriver(driver))
+ if (!sfInstallDriver(driver))
error("Sound driver initialization failed");
switch (driverNum) {
@@ -214,11 +214,11 @@ void SoundManager::installDriver(int driverNum) {
byte *bankData = g_resourceManager->getResource(RES_BANK, driverNum, 0, true);
if (bankData) {
// Install the patch bank data
- _sfInstallPatchBank(driver, bankData);
+ sfInstallPatchBank(driver, bankData);
DEALLOCATE(bankData);
} else {
// Could not locate patch bank data, so unload the driver
- _sfUnInstallDriver(driver);
+ sfUnInstallDriver(driver);
// Unmute currently active sounds
for (Common::List<Sound *>::iterator i = _playList.begin(); i != _playList.end(); ++i)
@@ -260,7 +260,7 @@ void SoundManager::unInstallDriver(int driverNum) {
(*j)->mute(true);
// Uninstall the driver
- _sfUnInstallDriver(*i);
+ sfUnInstallDriver(*i);
// Re-orient all the loaded sounds
for (j = _soundList.begin(); j != _soundList.end(); ++j)
@@ -303,7 +303,7 @@ void SoundManager::unloadSound(int soundNum) {
}
int SoundManager::determineGroup(const byte *soundData) {
- return _sfDetermineGroup(soundData);
+ return sfDetermineGroup(soundData);
}
void SoundManager::checkResVersion(const byte *soundData) {
@@ -325,7 +325,7 @@ int SoundManager::extractLoop(const byte *soundData) {
}
void SoundManager::extractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum) {
- _sfExtractTrackInfo(trackInfo, soundData, groupNum);
+ sfExtractTrackInfo(trackInfo, soundData, groupNum);
}
void SoundManager::addToSoundList(Sound *sound) {
@@ -338,46 +338,46 @@ void SoundManager::removeFromSoundList(Sound *sound) {
}
void SoundManager::addToPlayList(Sound *sound) {
- _sfAddToPlayList(sound);
+ sfAddToPlayList(sound);
}
void SoundManager::removeFromPlayList(Sound *sound) {
if (_soundManager)
- _sfRemoveFromPlayList(sound);
+ sfRemoveFromPlayList(sound);
}
bool SoundManager::isOnPlayList(Sound *sound) {
- return _sfIsOnPlayList(sound);
+ return sfIsOnPlayList(sound);
}
void SoundManager::updateSoundVol(Sound *sound) {
- _sfUpdateVolume(sound);
+ sfUpdateVolume(sound);
}
void SoundManager::updateSoundPri(Sound *sound) {
- _sfUpdatePriority(sound);
+ sfUpdatePriority(sound);
}
void SoundManager::updateSoundLoop(Sound *sound) {
- _sfUpdateLoop(sound);
+ sfUpdateLoop(sound);
}
void SoundManager::rethinkVoiceTypes() {
Common::StackLock slock(sfManager()._serverSuspendedMutex);
- _sfRethinkVoiceTypes();
+ sfRethinkVoiceTypes();
}
-void SoundManager::_sfSoundServer() {
+void SoundManager::sfSoundServer() {
if (sfManager()._needToRethink) {
- _sfRethinkVoiceTypes();
+ sfRethinkVoiceTypes();
sfManager()._needToRethink = false;
} else {
- _sfDereferenceAll();
+ sfDereferenceAll();
}
// If the master volume has changed, update it
if (sfManager()._newVolume != sfManager()._masterVol)
- _sfSetMasterVol(sfManager()._newVolume);
+ sfSetMasterVol(sfManager()._newVolume);
// If a time index has been set for any sound, fast forward to it
SynchronizedList<Sound *>::iterator i;
@@ -385,14 +385,14 @@ void SoundManager::_sfSoundServer() {
Sound *s = *i;
if (s->_newTimeIndex != 0) {
s->mute(true);
- s->_soSetTimeIndex(s->_newTimeIndex);
+ s->soSetTimeIndex(s->_newTimeIndex);
s->mute(false);
s->_newTimeIndex = 0;
}
}
// Handle any fading if necessary
- _sfProcessFading();
+ sfProcessFading();
// Poll all sound drivers in case they need it
for (Common::List<SoundDriver *>::iterator j = sfManager()._installedDrivers.begin();
@@ -401,7 +401,7 @@ void SoundManager::_sfSoundServer() {
}
}
-void SoundManager::_sfProcessFading() {
+void SoundManager::sfProcessFading() {
// Loop through processing active sounds
bool removeFlag = false;
Common::List<Sound *>::iterator i = sfManager()._playList.begin();
@@ -410,9 +410,9 @@ void SoundManager::_sfProcessFading() {
++i;
if (!s->_pausedCount)
- removeFlag = s->_soServiceTracks();
+ removeFlag = s->soServiceTracks();
if (removeFlag) {
- _sfDoRemoveFromPlayList(s);
+ sfDoRemoveFromPlayList(s);
s->_stoppedAsynchronously = true;
sfManager()._needToRethink = true;
}
@@ -429,13 +429,13 @@ void SoundManager::_sfProcessFading() {
s->_volume + s->_fadeSteps : s->_fadeDest;
}
- _sfDoUpdateVolume(s);
+ sfDoUpdateVolume(s);
if (s->_volume != s->_fadeDest)
s->_fadeCounter = s->_fadeTicks;
else {
s->_fadeDest = -1;
if (s->_stopAfterFadeFlag) {
- _sfDoRemoveFromPlayList(s);
+ sfDoRemoveFromPlayList(s);
s->_stoppedAsynchronously = true;
sfManager()._needToRethink = true;
}
@@ -475,7 +475,7 @@ bool SoundManager::isFading() {
return false;
}
-void SoundManager::_sfUpdateVoiceStructs() {
+void SoundManager::sfUpdateVoiceStructs() {
for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) {
VoiceTypeStruct *vs = sfManager()._voiceTypeStructPtrs[voiceIndex];
if (!vs)
@@ -504,7 +504,7 @@ void SoundManager::_sfUpdateVoiceStructs() {
}
}
-void SoundManager::_sfUpdateVoiceStructs2() {
+void SoundManager::sfUpdateVoiceStructs2() {
for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) {
VoiceTypeStruct *vtStruct = sfManager()._voiceTypeStructPtrs[voiceIndex];
if (!vtStruct)
@@ -528,7 +528,7 @@ void SoundManager::_sfUpdateVoiceStructs2() {
}
}
-void SoundManager::_sfUpdateCallback(void *ref) {
+void SoundManager::sfUpdateCallback(void *ref) {
((SoundManager *)ref)->update();
}
@@ -587,7 +587,7 @@ SoundManager &SoundManager::sfManager() {
return *_soundManager;
}
-int SoundManager::_sfDetermineGroup(const byte *soundData) {
+int SoundManager::sfDetermineGroup(const byte *soundData) {
const byte *p = soundData + READ_LE_UINT16(soundData + 8);
uint32 v;
while ((v = READ_LE_UINT32(p)) != 0) {
@@ -600,22 +600,22 @@ int SoundManager::_sfDetermineGroup(const byte *soundData) {
return 0;
}
-void SoundManager::_sfAddToPlayList(Sound *sound) {
+void SoundManager::sfAddToPlayList(Sound *sound) {
Common::StackLock slock(sfManager()._serverSuspendedMutex);
- _sfDoAddToPlayList(sound);
+ sfDoAddToPlayList(sound);
sound->_stoppedAsynchronously = false;
- _sfRethinkVoiceTypes();
+ sfRethinkVoiceTypes();
}
-void SoundManager::_sfRemoveFromPlayList(Sound *sound) {
+void SoundManager::sfRemoveFromPlayList(Sound *sound) {
Common::StackLock slock(sfManager()._serverSuspendedMutex);
- if (_sfDoRemoveFromPlayList(sound))
- _sfRethinkVoiceTypes();
+ if (sfDoRemoveFromPlayList(sound))
+ sfRethinkVoiceTypes();
}
-bool SoundManager::_sfIsOnPlayList(Sound *sound) {
+bool SoundManager::sfIsOnPlayList(Sound *sound) {
Common::StackLock slock(sfManager()._serverSuspendedMutex);
bool result = contains(_soundManager->_playList, sound);
@@ -623,7 +623,7 @@ bool SoundManager::_sfIsOnPlayList(Sound *sound) {
return result;
}
-void SoundManager::_sfRethinkSoundDrivers() {
+void SoundManager::sfRethinkSoundDrivers() {
// Free any existing entries
int idx;
@@ -643,7 +643,7 @@ void SoundManager::_sfRethinkSoundDrivers() {
i != sfManager()._installedDrivers.end(); ++i) {
// Process the group data for each sound driver
SoundDriver *driver = *i;
- const byte *groupData = driver->_groupOffset->pData;
+ const byte *groupData = driver->_groupOffset->_pData;
while (*groupData != 0xff) {
byte byteVal = *groupData++;
@@ -690,7 +690,7 @@ void SoundManager::_sfRethinkSoundDrivers() {
i != sfManager()._installedDrivers.end(); ++i) {
// Process the group data for each sound driver
SoundDriver *driver = *i;
- const byte *groupData = driver->_groupOffset->pData;
+ const byte *groupData = driver->_groupOffset->_pData;
while (*groupData != 0xff) {
byte byteVal = *groupData++;
@@ -746,8 +746,8 @@ void SoundManager::_sfRethinkSoundDrivers() {
}
}
-void SoundManager::_sfRethinkVoiceTypes() {
- _sfDereferenceAll();
+void SoundManager::sfRethinkVoiceTypes() {
+ sfDereferenceAll();
// Pre-processing
for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) {
@@ -797,7 +797,7 @@ void SoundManager::_sfRethinkVoiceTypes() {
if ((sound->_mutedCount != 0) || (sound->_pausedCount != 0))
continue;
- _sfUpdateVoiceStructs();
+ sfUpdateVoiceStructs();
Common::fill(sound->_chWork, sound->_chWork + SOUND_ARR_SIZE, false);
for (;;) {
@@ -831,7 +831,7 @@ void SoundManager::_sfRethinkVoiceTypes() {
if (foundPriority)
continue;
- _sfUpdateVoiceStructs2();
+ sfUpdateVoiceStructs2();
break;
}
@@ -860,7 +860,7 @@ void SoundManager::_sfRethinkVoiceTypes() {
maxPriority = MAX(maxPriority, vtStruct->_entries[idx]._type1._priority2);
if (!maxPriority) {
- _sfUpdateVoiceStructs2();
+ sfUpdateVoiceStructs2();
break;
}
@@ -944,7 +944,7 @@ void SoundManager::_sfRethinkVoiceTypes() {
continue;
}
- _sfUpdateVoiceStructs2();
+ sfUpdateVoiceStructs2();
break;
} else {
// Channel mode 1 handling (loc_23FAC)
@@ -973,7 +973,7 @@ void SoundManager::_sfRethinkVoiceTypes() {
if (foundPriority)
continue;
if (entryIndex == -1) {
- _sfUpdateVoiceStructs2();
+ sfUpdateVoiceStructs2();
break;
}
}
@@ -1008,7 +1008,7 @@ void SoundManager::_sfRethinkVoiceTypes() {
continue;
}
- _sfUpdateVoiceStructs2();
+ sfUpdateVoiceStructs2();
break;
}
@@ -1051,7 +1051,7 @@ void SoundManager::_sfRethinkVoiceTypes() {
if (!foundPriority)
continue;
if (priorityIndex == -1) {
- _sfUpdateVoiceStructs2();
+ sfUpdateVoiceStructs2();
break;
}
@@ -1271,38 +1271,38 @@ void SoundManager::_sfRethinkVoiceTypes() {
}
}
-void SoundManager::_sfUpdateVolume(Sound *sound) {
- _sfDereferenceAll();
- _sfDoUpdateVolume(sound);
+void SoundManager::sfUpdateVolume(Sound *sound) {
+ sfDereferenceAll();
+ sfDoUpdateVolume(sound);
}
-void SoundManager::_sfDereferenceAll() {
+void SoundManager::sfDereferenceAll() {
// Orignal used handles for both the driver list and voiceTypeStructPtrs list. This method then refreshed
// pointer lists based on the handles. Since in ScummVM we're just using pointers directly, this
// method doesn't need any implementation
}
-void SoundManager::_sfUpdatePriority(Sound *sound) {
+void SoundManager::sfUpdatePriority(Sound *sound) {
Common::StackLock slock(sfManager()._serverSuspendedMutex);
int tempPriority = (sound->_fixedPriority == 255) ? sound->_sndResPriority : sound->_priority;
if (sound->_priority != tempPriority) {
sound->_priority = tempPriority;
- if (_sfDoRemoveFromPlayList(sound)) {
- _sfDoAddToPlayList(sound);
- _sfRethinkVoiceTypes();
+ if (sfDoRemoveFromPlayList(sound)) {
+ sfDoAddToPlayList(sound);
+ sfRethinkVoiceTypes();
}
}
}
-void SoundManager::_sfUpdateLoop(Sound *sound) {
+void SoundManager::sfUpdateLoop(Sound *sound) {
if (sound->_fixedLoop)
sound->_loop = sound->_sndResLoop;
else
sound->_loop = sound->_fixedLoop;
}
-void SoundManager::_sfSetMasterVol(int volume) {
+void SoundManager::sfSetMasterVol(int volume) {
if (volume > 127)
volume = 127;
@@ -1316,7 +1316,7 @@ void SoundManager::_sfSetMasterVol(int volume) {
}
}
-void SoundManager::_sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum) {
+void SoundManager::sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum) {
trackInfo->_numTracks = 0;
const byte *p = soundData + READ_LE_UINT16(soundData + 8);
@@ -1345,11 +1345,11 @@ void SoundManager::_sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *s
}
}
-void SoundManager::_sfTerminate() {
+void SoundManager::sfTerminate() {
}
-void SoundManager::_sfExtractGroupMask() {
+void SoundManager::sfExtractGroupMask() {
uint32 mask = 0;
for (Common::List<SoundDriver *>::iterator i = sfManager()._installedDrivers.begin();
@@ -1359,37 +1359,37 @@ void SoundManager::_sfExtractGroupMask() {
_soundManager->_groupsAvail = mask;
}
-bool SoundManager::_sfInstallDriver(SoundDriver *driver) {
+bool SoundManager::sfInstallDriver(SoundDriver *driver) {
if (!driver->open())
return false;
sfManager()._installedDrivers.push_back(driver);
driver->_groupOffset = driver->getGroupData();
- driver->_groupMask = driver->_groupOffset->groupMask;
+ driver->_groupMask = driver->_groupOffset->_groupMask;
- _sfExtractGroupMask();
- _sfRethinkSoundDrivers();
+ sfExtractGroupMask();
+ sfRethinkSoundDrivers();
driver->setMasterVolume(sfManager()._masterVol);
return true;
}
-void SoundManager::_sfUnInstallDriver(SoundDriver *driver) {
+void SoundManager::sfUnInstallDriver(SoundDriver *driver) {
sfManager()._installedDrivers.remove(driver);
delete driver;
- _sfExtractGroupMask();
- _sfRethinkSoundDrivers();
+ sfExtractGroupMask();
+ sfRethinkSoundDrivers();
}
-void SoundManager::_sfInstallPatchBank(SoundDriver *driver, const byte *bankData) {
+void SoundManager::sfInstallPatchBank(SoundDriver *driver, const byte *bankData) {
driver->installPatch(bankData, g_vm->_memoryManager.getSize(bankData));
}
/**
* Adds the specified sound in the playing sound list, inserting in order of priority
*/
-void SoundManager::_sfDoAddToPlayList(Sound *sound) {
+void SoundManager::sfDoAddToPlayList(Sound *sound) {
Common::StackLock slock2(sfManager()._serverSuspendedMutex);
Common::List<Sound *>::iterator i = sfManager()._playList.begin();
@@ -1402,7 +1402,7 @@ void SoundManager::_sfDoAddToPlayList(Sound *sound) {
/**
* Removes the specified sound from the play list
*/
-bool SoundManager::_sfDoRemoveFromPlayList(Sound *sound) {
+bool SoundManager::sfDoRemoveFromPlayList(Sound *sound) {
Common::StackLock slock(sfManager()._serverSuspendedMutex);
bool result = false;
@@ -1417,7 +1417,7 @@ bool SoundManager::_sfDoRemoveFromPlayList(Sound *sound) {
return result;
}
-void SoundManager::_sfDoUpdateVolume(Sound *sound) {
+void SoundManager::sfDoUpdateVolume(Sound *sound) {
Common::StackLock slock(sfManager()._serverSuspendedMutex);
for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) {
@@ -1583,7 +1583,7 @@ void Sound::_prime(int soundResID, bool dontQueue) {
_remoteReceiver = ALLOCATE(200);
}
- _soPrimeSound(dontQueue);
+ soPrimeSound(dontQueue);
if (!dontQueue)
_soundManager->addToSoundList(this);
@@ -1767,7 +1767,7 @@ void Sound::release() {
_hold = -1;
}
-void Sound::_soPrimeSound(bool dontQueue) {
+void Sound::soPrimeSound(bool dontQueue) {
if (!dontQueue) {
_priority = (_fixedPriority != -1) ? _fixedPriority : _sndResPriority;
_loop = !_fixedLoop ? _fixedLoop : _sndResLoop;
@@ -1785,21 +1785,21 @@ void Sound::_soPrimeSound(bool dontQueue) {
_timer = 0;
_newTimeIndex = 0;
_loopTimer = 0;
- _soPrimeChannelData();
+ soPrimeChannelData();
}
-void Sound::_soSetTimeIndex(uint timeIndex) {
+void Sound::soSetTimeIndex(uint timeIndex) {
Common::StackLock slock(g_globals->_soundManager._serverSuspendedMutex);
if (timeIndex != _timer) {
_soundManager->_soTimeIndexFlag = true;
_timer = 0;
_loopTimer = 0;
- _soPrimeChannelData();
+ soPrimeChannelData();
while (timeIndex > 0) {
- if (_soServiceTracks()) {
- SoundManager::_sfDoRemoveFromPlayList(this);
+ if (soServiceTracks()) {
+ SoundManager::sfDoRemoveFromPlayList(this);
_stoppedAsynchronously = true;
_soundManager->_needToRethink = true;
break;
@@ -1812,9 +1812,9 @@ void Sound::_soSetTimeIndex(uint timeIndex) {
}
}
-bool Sound::_soServiceTracks() {
+bool Sound::soServiceTracks() {
if (_isEmpty) {
- _soRemoteReceive();
+ soRemoteReceive();
return false;
}
@@ -1823,9 +1823,9 @@ bool Sound::_soServiceTracks() {
int mode = *_channelData[trackCtr];
if (mode == 0) {
- _soServiceTrackType0(trackCtr, _channelData[trackCtr]);
+ soServiceTrackType0(trackCtr, _channelData[trackCtr]);
} else if (mode == 1) {
- _soServiceTrackType1(trackCtr, _channelData[trackCtr]);
+ soServiceTrackType1(trackCtr, _channelData[trackCtr]);
} else {
error("Unknown sound mode encountered");
}
@@ -1851,7 +1851,7 @@ bool Sound::_soServiceTracks() {
}
}
-void Sound::_soPrimeChannelData() {
+void Sound::soPrimeChannelData() {
if (_isEmpty) {
for (int idx = 0; idx < 16; ++idx) {
_chProgram[idx] = 0;
@@ -1917,11 +1917,11 @@ void Sound::_soPrimeChannelData() {
}
}
-void Sound::_soRemoteReceive() {
- error("_soRemoteReceive not implemented");
+void Sound::soRemoteReceive() {
+ error("soRemoteReceive not implemented");
}
-void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) {
+void Sound::soServiceTrackType0(int trackIndex, const byte *channelData) {
if (_trkRest[trackIndex]) {
--_trkRest[trackIndex];
return;
@@ -1970,7 +1970,7 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) {
// Only do processing if fast forwarding to a given time index
if (channelNum != -1) {
if (voiceType == VOICETYPE_1) {
- _soUpdateDamper(vtStruct, channelNum, chVoiceType, v);
+ soUpdateDamper(vtStruct, channelNum, chVoiceType, v);
} else if (voiceNum != -1) {
assert(driver);
driver->proc18(voiceNum, chVoiceType);
@@ -1991,9 +1991,9 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) {
if (channelNum != -1) {
if (voiceType != VOICETYPE_0) {
if (chFlags & 0x10)
- _soPlaySound2(vtStruct, channelData, channelNum, chVoiceType, v);
+ soPlaySound2(vtStruct, channelData, channelNum, chVoiceType, v);
else
- _soPlaySound(vtStruct, channelData, channelNum, chVoiceType, v, b);
+ soPlaySound(vtStruct, channelData, channelNum, chVoiceType, v, b);
} else if (voiceNum != -1) {
assert(driver);
driver->proc20(voiceNum, chVoiceType);
@@ -2030,17 +2030,17 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) {
int cmdVal = cmdList[v];
if (channelNum == -1) {
- if (_soDoUpdateTracks(cmdVal, b))
+ if (soDoUpdateTracks(cmdVal, b))
return;
} else {
- _soDoTrackCommand(_trkChannel[trackIndex], cmdVal, b);
+ soDoTrackCommand(_trkChannel[trackIndex], cmdVal, b);
if (!_soundManager->_soTimeIndexFlag) {
if (cmdVal == 7)
b = static_cast<byte>(_volume * (int)b / 127);
if (voiceType != VOICETYPE_0) {
- _soProc38(vtStruct, channelNum, chVoiceType, cmdVal, b);
+ soProc38(vtStruct, channelNum, chVoiceType, cmdVal, b);
} else if (voiceNum != -1) {
assert(driver);
driver->proc24(voiceNum, chVoiceType, this, cmdVal, b);
@@ -2067,17 +2067,17 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) {
int value = *pData++;
if (channelNum != -1) {
- _soDoTrackCommand(_trkChannel[trackIndex], cmd, value);
+ soDoTrackCommand(_trkChannel[trackIndex], cmd, value);
if (!_soundManager->_soTimeIndexFlag) {
if (voiceType != VOICETYPE_0) {
- _soProc38(vtStruct, channelNum, chVoiceType, cmd, value);
+ soProc38(vtStruct, channelNum, chVoiceType, cmd, value);
} else if (voiceNum != -1) {
assert(driver);
driver->proc24(voiceNum, chVoiceType, this, cmd, value);
}
}
- } else if (_soDoUpdateTracks(cmd, value)) {
+ } else if (soDoUpdateTracks(cmd, value)) {
return;
}
} else if (!(v & 0x2)) {
@@ -2091,7 +2091,7 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) {
_chPitchBlend[channel] = pitchBlend;
if (voiceType != VOICETYPE_0) {
- _soProc40(vtStruct, channelNum, pitchBlend);
+ soProc40(vtStruct, channelNum, pitchBlend);
} else if (voiceNum != -1) {
assert(driver);
driver->setPitchBlend(channel, pitchBlend);
@@ -2115,7 +2115,7 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) {
}
}
} else {
- _soSetTrackPos(trackIndex, pData - channelData, program);
+ soSetTrackPos(trackIndex, pData - channelData, program);
}
} else {
@@ -2139,7 +2139,7 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) {
}
}
-void Sound::_soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0) {
+void Sound::soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0) {
bool hasDamper = _chDamper[channelNum] != 0;
for (uint idx = 0; idx < voiceType->_entries.size(); ++idx) {
@@ -2161,8 +2161,8 @@ void Sound::_soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceTyp
}
}
-void Sound::_soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1) {
- int entryIndex = _soFindSound(vtStruct, channelNum);
+void Sound::soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1) {
+ int entryIndex = soFindSound(vtStruct, channelNum);
if (entryIndex != -1) {
SoundDriver *driver = vtStruct->_entries[entryIndex]._driver;
assert(driver);
@@ -2175,11 +2175,11 @@ void Sound::_soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int
}
}
-void Sound::_soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0) {
+void Sound::soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0) {
for (int trackCtr = 0; trackCtr < _trackInfo._numTracks; ++trackCtr) {
const byte *instrument = _channelData[trackCtr];
if ((*(instrument + 13) == v0) && (*instrument == 1)) {
- int entryIndex = _soFindSound(vtStruct, channelNum);
+ int entryIndex = soFindSound(vtStruct, channelNum);
if (entryIndex != -1) {
SoundDriver *driver = vtStruct->_entries[entryIndex]._driver;
@@ -2199,7 +2199,7 @@ void Sound::_soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, in
}
}
-void Sound::_soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value) {
+void Sound::soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value) {
if (cmd == 64) {
if (value == 0) {
for (uint entryIndex = 0; entryIndex < vtStruct->_entries.size(); ++entryIndex) {
@@ -2231,7 +2231,7 @@ void Sound::_soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voice
}
}
-void Sound::_soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend) {
+void Sound::soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend) {
for (uint entryIndex = 0; entryIndex < vtStruct->_entries.size(); ++entryIndex) {
VoiceStructEntryType1 &vte = vtStruct->_entries[entryIndex]._type1;
@@ -2244,7 +2244,7 @@ void Sound::_soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend)
}
}
-void Sound::_soDoTrackCommand(int channelNum, int command, int value) {
+void Sound::soDoTrackCommand(int channelNum, int command, int value) {
switch (command) {
case 1:
_chModulation[channelNum] = value;
@@ -2264,7 +2264,7 @@ void Sound::_soDoTrackCommand(int channelNum, int command, int value) {
}
}
-bool Sound::_soDoUpdateTracks(int command, int value) {
+bool Sound::soDoUpdateTracks(int command, int value) {
if ((command == 76) || (_hold != value))
return false;
@@ -2278,7 +2278,7 @@ bool Sound::_soDoUpdateTracks(int command, int value) {
return true;
}
-void Sound::_soSetTrackPos(int trackIndex, int trackPos, int cueValue) {
+void Sound::soSetTrackPos(int trackIndex, int trackPos, int cueValue) {
_trkIndex[trackIndex] = trackPos;
if (cueValue == 127) {
if (!_soundManager->_soTimeIndexFlag)
@@ -2294,7 +2294,7 @@ void Sound::_soSetTrackPos(int trackIndex, int trackPos, int cueValue) {
}
}
-void Sound::_soServiceTrackType1(int trackIndex, const byte *channelData) {
+void Sound::soServiceTrackType1(int trackIndex, const byte *channelData) {
if (_soundManager->_soTimeIndexFlag || !_trkState[trackIndex])
return;
@@ -2310,7 +2310,7 @@ void Sound::_soServiceTrackType1(int trackIndex, const byte *channelData) {
else {
if (vtStruct->_voiceType != VOICETYPE_0) {
if (_trkState[trackIndex] == 1) {
- int entryIndex = _soFindSound(vtStruct, *(channelData + 1));
+ int entryIndex = soFindSound(vtStruct, *(channelData + 1));
if (entryIndex != -1) {
SoundDriver *driver = vtStruct->_entries[entryIndex]._driver;
assert(driver);
@@ -2352,7 +2352,7 @@ void Sound::_soServiceTrackType1(int trackIndex, const byte *channelData) {
}
}
-int Sound::_soFindSound(VoiceTypeStruct *vtStruct, int channelNum) {
+int Sound::soFindSound(VoiceTypeStruct *vtStruct, int channelNum) {
int entryIndex = -1, entry2Index = -1;
int v6 = 0, v8 = 0;
@@ -2539,10 +2539,10 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() {
_maxVersion = 0x10A;
_masterVolume = 0;
- _groupData.groupMask = 9;
- _groupData.v1 = 0x46;
- _groupData.v2 = 0;
- _groupData.pData = &adlib_group_data[0];
+ _groupData._groupMask = 9;
+ _groupData._v1 = 0x46;
+ _groupData._v2 = 0;
+ _groupData._pData = &adlib_group_data[0];
_mixer = g_vm->_mixer;
_sampleRate = _mixer->getOutputRate();
@@ -2823,7 +2823,7 @@ int AdlibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
memset(buffer, 0, sizeof(int16) * numSamples);
while (samplesLeft) {
if (!_samplesTillCallback) {
- SoundManager::_sfUpdateCallback(NULL);
+ SoundManager::sfUpdateCallback(NULL);
flush();
_samplesTillCallback = _samplesPerCallback;
@@ -2852,11 +2852,11 @@ SoundBlasterDriver::SoundBlasterDriver(): SoundDriver() {
_maxVersion = 0x10A;
_masterVolume = 0;
- _groupData.groupMask = 1;
- _groupData.v1 = 0x3E;
- _groupData.v2 = 0;
+ _groupData._groupMask = 1;
+ _groupData._v1 = 0x3E;
+ _groupData._v2 = 0;
static byte const group_data[] = { 3, 1, 1, 0, 0xff };
- _groupData.pData = group_data;
+ _groupData._pData = group_data;
_mixer = g_vm->_mixer;
_sampleRate = _mixer->getOutputRate();
diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h
index 77d1f3d3ac..2f59afb49b 100644
--- a/engines/tsage/sound.h
+++ b/engines/tsage/sound.h
@@ -54,18 +54,18 @@ enum VoiceType {VOICETYPE_0 = 0, VOICETYPE_1 = 1};
class SoundDriverEntry {
public:
- int driverNum;
- SoundDriverStatus status;
- int field2, field6;
- Common::String shortDescription;
- Common::String longDescription;
+ int _driverNum;
+ SoundDriverStatus _status;
+ int _field2, _field6;
+ Common::String _shortDescription;
+ Common::String _longDescription;
};
struct GroupData {
- uint32 groupMask;
- byte v1;
- byte v2;
- const byte *pData;
+ uint32 _groupMask;
+ byte _v1;
+ byte _v2;
+ const byte *_pData;
};
struct RegisterValue {
@@ -229,31 +229,31 @@ public:
// _sf methods
static SoundManager &sfManager();
- static void _sfTerminate();
- static int _sfDetermineGroup(const byte *soundData);
- static void _sfAddToPlayList(Sound *sound);
- static void _sfRemoveFromPlayList(Sound *sound);
- static bool _sfIsOnPlayList(Sound *sound);
- static void _sfRethinkSoundDrivers();
- static void _sfRethinkVoiceTypes();
- static void _sfUpdateVolume(Sound *sound);
- static void _sfDereferenceAll();
- static void _sfUpdatePriority(Sound *sound);
- static void _sfUpdateLoop(Sound *sound);
- static void _sfSetMasterVol(int volume);
- static void _sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum);
- static void _sfExtractGroupMask();
- static bool _sfInstallDriver(SoundDriver *driver);
- static void _sfUnInstallDriver(SoundDriver *driver);
- static void _sfInstallPatchBank(SoundDriver *driver, const byte *bankData);
- static void _sfDoAddToPlayList(Sound *sound);
- static bool _sfDoRemoveFromPlayList(Sound *sound);
- static void _sfDoUpdateVolume(Sound *sound);
- static void _sfSoundServer();
- static void _sfProcessFading();
- static void _sfUpdateVoiceStructs();
- static void _sfUpdateVoiceStructs2();
- static void _sfUpdateCallback(void *ref);
+ static void sfTerminate();
+ static int sfDetermineGroup(const byte *soundData);
+ static void sfAddToPlayList(Sound *sound);
+ static void sfRemoveFromPlayList(Sound *sound);
+ static bool sfIsOnPlayList(Sound *sound);
+ static void sfRethinkSoundDrivers();
+ static void sfRethinkVoiceTypes();
+ static void sfUpdateVolume(Sound *sound);
+ static void sfDereferenceAll();
+ static void sfUpdatePriority(Sound *sound);
+ static void sfUpdateLoop(Sound *sound);
+ static void sfSetMasterVol(int volume);
+ static void sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum);
+ static void sfExtractGroupMask();
+ static bool sfInstallDriver(SoundDriver *driver);
+ static void sfUnInstallDriver(SoundDriver *driver);
+ static void sfInstallPatchBank(SoundDriver *driver, const byte *bankData);
+ static void sfDoAddToPlayList(Sound *sound);
+ static bool sfDoRemoveFromPlayList(Sound *sound);
+ static void sfDoUpdateVolume(Sound *sound);
+ static void sfSoundServer();
+ static void sfProcessFading();
+ static void sfUpdateVoiceStructs();
+ static void sfUpdateVoiceStructs2();
+ static void sfUpdateCallback(void *ref);
};
class Sound: public EventHandler {
@@ -343,23 +343,23 @@ public:
void orientAfterDriverChange();
// _so methods
- void _soPrimeSound(bool dontQueue);
- void _soSetTimeIndex(uint timeIndex);
- bool _soServiceTracks();
- void _soPrimeChannelData();
- void _soRemoteReceive();
- void _soServiceTrackType0(int trackIndex, const byte *channelData);
- void _soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0);
- void _soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1);
- void _soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0);
- void _soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value);
- void _soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend);
- void _soDoTrackCommand(int channelNum, int command, int value);
- bool _soDoUpdateTracks(int command, int value);
- void _soSetTrackPos(int trackIndex, int trackPos, int cueValue);
-
- void _soServiceTrackType1(int trackIndex, const byte *channelData);
- int _soFindSound(VoiceTypeStruct *vtStruct, int channelNum);
+ void soPrimeSound(bool dontQueue);
+ void soSetTimeIndex(uint timeIndex);
+ bool soServiceTracks();
+ void soPrimeChannelData();
+ void soRemoteReceive();
+ void soServiceTrackType0(int trackIndex, const byte *channelData);
+ void soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0);
+ void soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1);
+ void soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0);
+ void soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value);
+ void soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend);
+ void soDoTrackCommand(int channelNum, int command, int value);
+ bool soDoUpdateTracks(int command, int value);
+ void soSetTrackPos(int trackIndex, int trackPos, int cueValue);
+
+ void soServiceTrackType1(int trackIndex, const byte *channelData);
+ int soFindSound(VoiceTypeStruct *vtStruct, int channelNum);
};
class ASound: public EventHandler {
diff --git a/engines/tsage/tsage.cpp b/engines/tsage/tsage.cpp
index 40f4dfcfd2..87697f950b 100644
--- a/engines/tsage/tsage.cpp
+++ b/engines/tsage/tsage.cpp
@@ -45,7 +45,7 @@ TSageEngine::TSageEngine(OSystem *system, const tSageGameDescription *gameDesc)
else if (g_vm->getGameID() == GType_BlueForce)
_debugger = new BlueForceDebugger();
else if (g_vm->getGameID() == GType_Ringworld2)
- _debugger = new Ringworld2Debugger();
+ _debugger = new Ringworld2Debugger();
}
Common::Error TSageEngine::init() {
@@ -92,7 +92,7 @@ void TSageEngine::initialize() {
g_resourceManager->addLib("TSAGE.RLB");
}
g_globals = new BlueForce::BlueForceGlobals();
-
+
// Setup the user interface
T2_GLOBALS._uiElements.setup(Common::Point(0, UI_INTERFACE_Y - 2));
@@ -107,7 +107,7 @@ void TSageEngine::initialize() {
// Reset all global variables
R2_GLOBALS.reset();
- }
+ }
g_globals->gfxManager().setDefaults();
diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp
index bee09f7391..1b04f3fae9 100644
--- a/engines/tucker/resource.cpp
+++ b/engines/tucker/resource.cpp
@@ -29,6 +29,9 @@
#include "audio/decoders/vorbis.h"
#include "audio/decoders/wave.h"
+#include "graphics/surface.h"
+#include "graphics/decoders/pcx.h"
+
#include "tucker/tucker.h"
#include "tucker/graphics.h"
@@ -298,23 +301,21 @@ void TuckerEngine::loadImage(const char *fname, uint8 *dst, int type) {
return;
}
}
- f.seek(128, SEEK_SET);
- int size = 0;
- while (size < 64000) {
- int code = f.readByte();
- if (code >= 0xC0) {
- const int sz = code - 0xC0;
- code = f.readByte();
- memset(dst + size, code, sz);
- size += sz;
- } else {
- dst[size++] = code;
- }
- }
+
+ ::Graphics::PCXDecoder pcx;
+ if (!pcx.loadStream(f))
+ error("Error while reading PCX image");
+
+ const ::Graphics::Surface *pcxSurface = pcx.getSurface();
+ if (pcxSurface->format.bytesPerPixel != 1)
+ error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel);
+ if (pcxSurface->w != 320 || pcxSurface->h != 200)
+ error("Invalid PCX surface size (%d x %d)", pcxSurface->w, pcxSurface->h);
+ for (uint16 y = 0; y < pcxSurface->h; y++)
+ memcpy(dst + y * 320, pcxSurface->getBasePtr(0, y), pcxSurface->w);
+
if (type != 0) {
- if (f.readByte() != 12)
- return;
- f.read(_currentPalette, 768);
+ memcpy(_currentPalette, pcx.getPalette(), 3 * 256);
setBlackPalette();
}
}
diff --git a/engines/wintermute/ad/ad_actor.cpp b/engines/wintermute/ad/ad_actor.cpp
new file mode 100644
index 0000000000..d175855d1e
--- /dev/null
+++ b/engines/wintermute/ad/ad_actor.cpp
@@ -0,0 +1,1460 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_actor.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/ad/ad_entity.h"
+#include "engines/wintermute/ad/ad_sprite_set.h"
+#include "engines/wintermute/ad/ad_waypoint_group.h"
+#include "engines/wintermute/ad/ad_path.h"
+#include "engines/wintermute/ad/ad_sentence.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/particles/part_emitter.h"
+#include "engines/wintermute/base/base_engine.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdActor, false)
+
+
+//////////////////////////////////////////////////////////////////////////
+AdActor::AdActor(BaseGame *inGame) : AdTalkHolder(inGame) {
+ _path = new AdPath(_gameRef);
+
+ _type = OBJECT_ACTOR;
+ _dir = DI_LEFT;
+
+ _walkSprite = NULL;
+ _standSprite = NULL;
+ _turnLeftSprite = NULL;
+ _turnRightSprite = NULL;
+
+ _targetPoint = new BasePoint;
+ _afterWalkDir = DI_NONE;
+
+ _animSprite2 = NULL;
+
+ setDefaultAnimNames();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::setDefaultAnimNames() {
+ _talkAnimName = "talk";
+ _idleAnimName = "idle";
+ _walkAnimName = "walk";
+ _turnLeftAnimName = "turnleft";
+ _turnRightAnimName = "turnright";
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AdActor::~AdActor() {
+ delete _path;
+ delete _targetPoint;
+ _path = NULL;
+ _targetPoint = NULL;
+
+ delete _walkSprite;
+ delete _standSprite;
+ delete _turnLeftSprite;
+ delete _turnRightSprite;
+ _walkSprite = NULL;
+ _standSprite = NULL;
+ _turnLeftSprite = NULL;
+ _turnRightSprite = NULL;
+
+ _animSprite2 = NULL; // ref only
+
+ for (uint32 i = 0; i < _talkSprites.size(); i++) {
+ delete _talkSprites[i];
+ }
+ _talkSprites.clear();
+
+ for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
+ delete _talkSpritesEx[i];
+ }
+ _talkSpritesEx.clear();
+
+ for (uint32 i = 0; i < _anims.size(); i++) {
+ delete _anims[i];
+ _anims[i] = NULL;
+ }
+ _anims.clear();
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdActor::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing ACTOR file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(ACTOR)
+TOKEN_DEF(X)
+TOKEN_DEF(Y)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(NAME)
+TOKEN_DEF(SCALABLE)
+TOKEN_DEF(REGISTRABLE)
+TOKEN_DEF(INTERACTIVE)
+TOKEN_DEF(SHADOWABLE)
+TOKEN_DEF(COLORABLE)
+TOKEN_DEF(ACTIVE)
+TOKEN_DEF(WALK)
+TOKEN_DEF(STAND)
+TOKEN_DEF(TALK_SPECIAL)
+TOKEN_DEF(TALK)
+TOKEN_DEF(TURN_LEFT)
+TOKEN_DEF(TURN_RIGHT)
+TOKEN_DEF(EVENTS)
+TOKEN_DEF(FONT)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(SOUND_VOLUME)
+TOKEN_DEF(SOUND_PANNING)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(BLOCKED_REGION)
+TOKEN_DEF(WAYPOINTS)
+TOKEN_DEF(IGNORE_ITEMS)
+TOKEN_DEF(ROTABLE)
+TOKEN_DEF(ROTATABLE)
+TOKEN_DEF(ALPHA_COLOR)
+TOKEN_DEF(SCALE)
+TOKEN_DEF(RELATIVE_SCALE)
+TOKEN_DEF(ALPHA)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(ANIMATION)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ACTOR)
+ TOKEN_TABLE(X)
+ TOKEN_TABLE(Y)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(SCALABLE)
+ TOKEN_TABLE(REGISTRABLE)
+ TOKEN_TABLE(INTERACTIVE)
+ TOKEN_TABLE(SHADOWABLE)
+ TOKEN_TABLE(COLORABLE)
+ TOKEN_TABLE(ACTIVE)
+ TOKEN_TABLE(WALK)
+ TOKEN_TABLE(STAND)
+ TOKEN_TABLE(TALK_SPECIAL)
+ TOKEN_TABLE(TALK)
+ TOKEN_TABLE(TURN_LEFT)
+ TOKEN_TABLE(TURN_RIGHT)
+ TOKEN_TABLE(EVENTS)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(SOUND_VOLUME)
+ TOKEN_TABLE(SOUND_PANNING)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(BLOCKED_REGION)
+ TOKEN_TABLE(WAYPOINTS)
+ TOKEN_TABLE(IGNORE_ITEMS)
+ TOKEN_TABLE(ROTABLE)
+ TOKEN_TABLE(ROTATABLE)
+ TOKEN_TABLE(ALPHA_COLOR)
+ TOKEN_TABLE(SCALE)
+ TOKEN_TABLE(RELATIVE_SCALE)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(ANIMATION)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_ACTOR) {
+ _gameRef->LOG(0, "'ACTOR' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ AdGame *adGame = (AdGame *)_gameRef;
+ AdSpriteSet *spr = NULL;
+ int ar = 0, ag = 0, ab = 0, alpha = 0;
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_X:
+ parser.scanStr((char *)params, "%d", &_posX);
+ break;
+
+ case TOKEN_Y:
+ parser.scanStr((char *)params, "%d", &_posY);
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_FONT:
+ setFont((char *)params);
+ break;
+
+ case TOKEN_SCALABLE:
+ parser.scanStr((char *)params, "%b", &_zoomable);
+ break;
+
+ case TOKEN_ROTABLE:
+ case TOKEN_ROTATABLE:
+ parser.scanStr((char *)params, "%b", &_rotatable);
+ break;
+
+ case TOKEN_REGISTRABLE:
+ case TOKEN_INTERACTIVE:
+ parser.scanStr((char *)params, "%b", &_registrable);
+ break;
+
+ case TOKEN_SHADOWABLE:
+ case TOKEN_COLORABLE:
+ parser.scanStr((char *)params, "%b", &_shadowable);
+ break;
+
+ case TOKEN_ACTIVE:
+ parser.scanStr((char *)params, "%b", &_active);
+ break;
+
+ case TOKEN_WALK:
+ delete _walkSprite;
+ _walkSprite = NULL;
+ spr = new AdSpriteSet(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texWalkLifeTime, CACHE_HALF))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _walkSprite = spr;
+ }
+ break;
+
+ case TOKEN_TALK:
+ spr = new AdSpriteSet(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texTalkLifeTime))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _talkSprites.add(spr);
+ }
+ break;
+
+ case TOKEN_TALK_SPECIAL:
+ spr = new AdSpriteSet(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texTalkLifeTime))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _talkSpritesEx.add(spr);
+ }
+ break;
+
+ case TOKEN_STAND:
+ delete _standSprite;
+ _standSprite = NULL;
+ spr = new AdSpriteSet(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texStandLifeTime))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _standSprite = spr;
+ }
+ break;
+
+ case TOKEN_TURN_LEFT:
+ delete _turnLeftSprite;
+ _turnLeftSprite = NULL;
+ spr = new AdSpriteSet(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadBuffer(params, true))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _turnLeftSprite = spr;
+ }
+ break;
+
+ case TOKEN_TURN_RIGHT:
+ delete _turnRightSprite;
+ _turnRightSprite = NULL;
+ spr = new AdSpriteSet(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadBuffer(params, true))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _turnRightSprite = spr;
+ }
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_SOUND_VOLUME:
+ parser.scanStr((char *)params, "%d", &_sFXVolume);
+ break;
+
+ case TOKEN_SCALE: {
+ int s;
+ parser.scanStr((char *)params, "%d", &s);
+ _scale = (float)s;
+
+ }
+ break;
+
+ case TOKEN_RELATIVE_SCALE: {
+ int s;
+ parser.scanStr((char *)params, "%d", &s);
+ _relativeScale = (float)s;
+
+ }
+ break;
+
+ case TOKEN_SOUND_PANNING:
+ parser.scanStr((char *)params, "%b", &_autoSoundPanning);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+
+ case TOKEN_BLOCKED_REGION: {
+ delete _blockRegion;
+ delete _currentBlockRegion;
+ _blockRegion = NULL;
+ _currentBlockRegion = NULL;
+ BaseRegion *rgn = new BaseRegion(_gameRef);
+ BaseRegion *crgn = new BaseRegion(_gameRef);
+ if (!rgn || !crgn || DID_FAIL(rgn->loadBuffer(params, false))) {
+ delete _blockRegion;
+ delete _currentBlockRegion;
+ _blockRegion = NULL;
+ _currentBlockRegion = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ _blockRegion = rgn;
+ _currentBlockRegion = crgn;
+ _currentBlockRegion->mimic(_blockRegion);
+ }
+ }
+ break;
+
+ case TOKEN_WAYPOINTS: {
+ delete _wptGroup;
+ delete _currentWptGroup;
+ _wptGroup = NULL;
+ _currentWptGroup = NULL;
+ AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef);
+ AdWaypointGroup *cwpt = new AdWaypointGroup(_gameRef);
+ if (!wpt || !cwpt || DID_FAIL(wpt->loadBuffer(params, false))) {
+ delete _wptGroup;
+ delete _currentWptGroup;
+ _wptGroup = NULL;
+ _currentWptGroup = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ _wptGroup = wpt;
+ _currentWptGroup = cwpt;
+ _currentWptGroup->mimic(_wptGroup);
+ }
+ }
+ break;
+
+ case TOKEN_IGNORE_ITEMS:
+ parser.scanStr((char *)params, "%b", &_ignoreItems);
+ break;
+
+ case TOKEN_ALPHA_COLOR:
+ parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab);
+ break;
+
+ case TOKEN_ALPHA:
+ parser.scanStr((char *)params, "%d", &alpha);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+
+ case TOKEN_ANIMATION: {
+ AdSpriteSet *anim = new AdSpriteSet(_gameRef, this);
+ if (!anim || DID_FAIL(anim->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _anims.add(anim);
+ }
+ }
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in ACTOR definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ if (spr) {
+ delete spr;
+ }
+ _gameRef->LOG(0, "Error loading ACTOR definition");
+ return STATUS_FAILED;
+ }
+
+ if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) {
+ ar = ag = ab = 255;
+ }
+ _alphaColor = BYTETORGBA(ar, ag, ab, alpha);
+ _state = _nextState = STATE_READY;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdActor::turnTo(TDirection dir) {
+ int delta1, delta2, delta3, delta;
+
+ delta1 = dir - _dir;
+ delta2 = dir + NUM_DIRECTIONS - _dir;
+ delta3 = dir - NUM_DIRECTIONS - _dir;
+
+ delta1 = (abs(delta1) <= abs(delta2)) ? delta1 : delta2;
+ delta = (abs(delta1) <= abs(delta3)) ? delta1 : delta3;
+
+ // already there?
+ if (abs(delta) < 2) {
+ _dir = dir;
+ _state = _nextState;
+ _nextState = STATE_READY;
+ return;
+ }
+
+ _targetDir = dir;
+ _state = delta < 0 ? STATE_TURNING_LEFT : STATE_TURNING_RIGHT;
+
+ _tempSprite2 = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdActor::goTo(int x, int y, TDirection afterWalkDir) {
+ _afterWalkDir = afterWalkDir;
+ if (x == _targetPoint->x && y == _targetPoint->y && _state == STATE_FOLLOWING_PATH) {
+ return;
+ }
+
+ _path->reset();
+ _path->setReady(false);
+
+ _targetPoint->x = x;
+ _targetPoint->y = y;
+
+ ((AdGame *)_gameRef)->_scene->correctTargetPoint(_posX, _posY, &_targetPoint->x, &_targetPoint->y, true, this);
+
+ _state = STATE_SEARCHING_PATH;
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::display() {
+ if (_active) {
+ updateSounds();
+ }
+
+ uint32 alpha;
+ if (_alphaColor != 0) {
+ alpha = _alphaColor;
+ } else {
+ alpha = _shadowable ? ((AdGame *)_gameRef)->_scene->getAlphaAt(_posX, _posY, true) : 0xFFFFFFFF;
+ }
+
+ float scaleX, scaleY;
+ getScale(&scaleX, &scaleY);
+
+
+ float rotate;
+ if (_rotatable) {
+ if (_rotateValid) {
+ rotate = _rotate;
+ } else {
+ rotate = ((AdGame *)_gameRef)->_scene->getRotationAt(_posX, _posY) + _relativeRotate;
+ }
+ } else {
+ rotate = 0.0f;
+ }
+
+ if (_active) {
+ displaySpriteAttachments(true);
+ }
+
+ if (_currentSprite && _active) {
+ bool reg = _registrable;
+ if (_ignoreItems && ((AdGame *)_gameRef)->_selectedItem) {
+ reg = false;
+ }
+
+ _currentSprite->display(_posX,
+ _posY,
+ reg ? _registerAlias : NULL,
+ scaleX,
+ scaleY,
+ alpha,
+ rotate,
+ _blendMode);
+
+ }
+
+ if (_active) {
+ displaySpriteAttachments(false);
+ }
+ if (_active && _partEmitter) {
+ _partEmitter->display();
+ }
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::update() {
+ _currentSprite = NULL;
+
+ if (_state == STATE_READY) {
+ if (_animSprite) {
+ delete _animSprite;
+ _animSprite = NULL;
+ }
+ if (_animSprite2) {
+ _animSprite2 = NULL;
+ }
+ }
+
+ // finished playing animation?
+ if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) {
+ _state = _nextState;
+ _nextState = STATE_READY;
+ _currentSprite = _animSprite;
+ }
+
+ if (_state == STATE_PLAYING_ANIM_SET && _animSprite2 != NULL && _animSprite2->isFinished()) {
+ _state = _nextState;
+ _nextState = STATE_READY;
+ _currentSprite = _animSprite2;
+ }
+
+ if (_sentence && _state != STATE_TALKING) {
+ _sentence->finish();
+ }
+
+ // default: stand animation
+ if (!_currentSprite) {
+ if (_sprite) {
+ _currentSprite = _sprite;
+ } else {
+ if (_standSprite) {
+ _currentSprite = _standSprite->getSprite(_dir);
+ } else {
+ AdSpriteSet *anim = getAnimByName(_idleAnimName);
+ if (anim) {
+ _currentSprite = anim->getSprite(_dir);
+ }
+ }
+ }
+ }
+
+ bool already_moved = false;
+
+ switch (_state) {
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_PLAYING_ANIM:
+ _currentSprite = _animSprite;
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_PLAYING_ANIM_SET:
+ _currentSprite = _animSprite2;
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_TURNING_LEFT:
+ if (_tempSprite2 == NULL || _tempSprite2->isFinished()) {
+ if (_dir > 0) {
+ _dir = (TDirection)(_dir - 1);
+ } else {
+ _dir = (TDirection)(NUM_DIRECTIONS - 1);
+ }
+
+ if (_dir == _targetDir) {
+ _tempSprite2 = NULL;
+ _state = _nextState;
+ _nextState = STATE_READY;
+ } else {
+ if (_turnLeftSprite) {
+ _tempSprite2 = _turnLeftSprite->getSprite(_dir);
+ } else {
+ AdSpriteSet *anim = getAnimByName(_turnLeftAnimName);
+ if (anim) {
+ _tempSprite2 = anim->getSprite(_dir);
+ }
+ }
+
+ if (_tempSprite2) {
+ _tempSprite2->reset();
+ if (_tempSprite2->_looping) {
+ _tempSprite2->_looping = false;
+ }
+ }
+ _currentSprite = _tempSprite2;
+ }
+ } else {
+ _currentSprite = _tempSprite2;
+ }
+ break;
+
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_TURNING_RIGHT:
+ if (_tempSprite2 == NULL || _tempSprite2->isFinished()) {
+ _dir = (TDirection)(_dir + 1);
+
+ if ((int)_dir >= (int)NUM_DIRECTIONS) {
+ _dir = (TDirection)(0);
+ }
+
+ if (_dir == _targetDir) {
+ _tempSprite2 = NULL;
+ _state = _nextState;
+ _nextState = STATE_READY;
+ } else {
+ if (_turnRightSprite) {
+ _tempSprite2 = _turnRightSprite->getSprite(_dir);
+ } else {
+ AdSpriteSet *anim = getAnimByName(_turnRightAnimName);
+ if (anim) {
+ _tempSprite2 = anim->getSprite(_dir);
+ }
+ }
+
+ if (_tempSprite2) {
+ _tempSprite2->reset();
+ if (_tempSprite2->_looping) {
+ _tempSprite2->_looping = false;
+ }
+ }
+ _currentSprite = _tempSprite2;
+ }
+ } else {
+ _currentSprite = _tempSprite2;
+ }
+ break;
+
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_SEARCHING_PATH:
+ // keep asking scene for the path
+ if (((AdGame *)_gameRef)->_scene->getPath(BasePoint(_posX, _posY), *_targetPoint, _path, this)) {
+ _state = STATE_WAITING_PATH;
+ }
+ break;
+
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_WAITING_PATH:
+ // wait until the scene finished the path
+ if (_path->_ready) {
+ followPath();
+ }
+ break;
+
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_FOLLOWING_PATH:
+ getNextStep();
+ already_moved = true;
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_TALKING: {
+ _sentence->update(_dir);
+ if (_sentence->_currentSprite) {
+ _tempSprite2 = _sentence->_currentSprite;
+ }
+
+ bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime);
+ if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) {
+ if (timeIsUp) {
+ _sentence->finish();
+ _tempSprite2 = NULL;
+ _state = _nextState;
+ _nextState = STATE_READY;
+ } else {
+ _tempSprite2 = getTalkStance(_sentence->getNextStance());
+ if (_tempSprite2) {
+ _tempSprite2->reset();
+ _currentSprite = _tempSprite2;
+ ((AdGame *)_gameRef)->addSentence(_sentence);
+ }
+ }
+ } else {
+ _currentSprite = _tempSprite2;
+ ((AdGame *)_gameRef)->addSentence(_sentence);
+ }
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_READY:
+ if (!_animSprite && !_animSprite2) {
+ if (_sprite) {
+ _currentSprite = _sprite;
+ } else {
+ if (_standSprite) {
+ _currentSprite = _standSprite->getSprite(_dir);
+ } else {
+ AdSpriteSet *anim = getAnimByName(_idleAnimName);
+ if (anim) {
+ _currentSprite = anim->getSprite(_dir);
+ }
+ }
+ }
+ }
+ break;
+ default:
+ error("AdActor::Update - Unhandled enum");
+ }
+
+
+ if (_currentSprite && !already_moved) {
+ _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100);
+ if (_currentSprite->isChanged()) {
+ _posX += _currentSprite->_moveX;
+ _posY += _currentSprite->_moveY;
+ afterMove();
+ }
+ }
+
+ //_gameRef->QuickMessageForm("%s", _currentSprite->_filename);
+
+ updateBlockRegion();
+ _ready = (_state == STATE_READY);
+
+ updatePartEmitter();
+ updateSpriteAttachments();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdActor::followPath() {
+ // skip current position
+ _path->getFirst();
+ while (_path->getCurrent() != NULL) {
+ if (_path->getCurrent()->x != _posX || _path->getCurrent()->y != _posY) {
+ break;
+ }
+ _path->getNext();
+ }
+
+ // are there points to follow?
+ if (_path->getCurrent() != NULL) {
+ _state = STATE_FOLLOWING_PATH;
+ initLine(BasePoint(_posX, _posY), *_path->getCurrent());
+ } else {
+ if (_afterWalkDir != DI_NONE) {
+ turnTo(_afterWalkDir);
+ } else {
+ _state = STATE_READY;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdActor::getNextStep() {
+ if (_walkSprite) {
+ _currentSprite = _walkSprite->getSprite(_dir);
+ } else {
+ AdSpriteSet *anim = getAnimByName(_walkAnimName);
+ if (anim) {
+ _currentSprite = anim->getSprite(_dir);
+ }
+ }
+
+ if (!_currentSprite) {
+ return;
+ }
+
+ _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100);
+ if (!_currentSprite->isChanged()) {
+ return;
+ }
+
+
+ int maxStepX, maxStepY;
+ maxStepX = abs(_currentSprite->_moveX);
+ maxStepY = abs(_currentSprite->_moveY);
+
+ maxStepX = MAX(maxStepX, maxStepY);
+ maxStepX = MAX(maxStepX, 1);
+
+ while (_pFCount > 0 && maxStepX >= 0) {
+ _pFX += _pFStepX;
+ _pFY += _pFStepY;
+
+ _pFCount--;
+ maxStepX--;
+ }
+
+ if (((AdGame *)_gameRef)->_scene->isBlockedAt((int)_pFX, (int) _pFY, true, this)) {
+ if (_pFCount == 0) {
+ _state = _nextState;
+ _nextState = STATE_READY;
+ return;
+ }
+ goTo(_targetPoint->x, _targetPoint->y);
+ return;
+ }
+
+
+ _posX = (int)_pFX;
+ _posY = (int)_pFY;
+
+ afterMove();
+
+
+ if (_pFCount == 0) {
+ if (_path->getNext() == NULL) {
+ _posX = _targetPoint->x;
+ _posY = _targetPoint->y;
+
+ _path->reset();
+ if (_afterWalkDir != DI_NONE) {
+ turnTo(_afterWalkDir);
+ } else {
+ _state = _nextState;
+ _nextState = STATE_READY;
+ }
+ } else {
+ initLine(BasePoint(_posX, _posY), *_path->getCurrent());
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdActor::initLine(BasePoint startPt, BasePoint endPt) {
+ _pFCount = MAX((abs(endPt.x - startPt.x)) , (abs(endPt.y - startPt.y)));
+
+ _pFStepX = (double)(endPt.x - startPt.x) / _pFCount;
+ _pFStepY = (double)(endPt.y - startPt.y) / _pFCount;
+
+ _pFX = startPt.x;
+ _pFY = startPt.y;
+
+ int angle = (int)(atan2((double)(endPt.y - startPt.y), (double)(endPt.x - startPt.x)) * (180 / 3.14));
+
+ _nextState = STATE_FOLLOWING_PATH;
+
+ turnTo(angleToDirection(angle));
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // GoTo / GoToAsync
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GoTo") == 0 || strcmp(name, "GoToAsync") == 0) {
+ stack->correctParams(2);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+ goTo(x, y);
+ if (strcmp(name, "GoToAsync") != 0) {
+ script->waitForExclusive(this);
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GoToObject / GoToObjectAsync
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GoToObject") == 0 || strcmp(name, "GoToObjectAsync") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ if (!val->isNative()) {
+ script->runtimeError("actor.%s method accepts an entity refrence only", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ AdObject *obj = (AdObject *)val->getNative();
+ if (!obj || obj->_type != OBJECT_ENTITY) {
+ script->runtimeError("actor.%s method accepts an entity refrence only", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ AdEntity *ent = (AdEntity *)obj;
+ if (ent->_walkToX == 0 && ent->_walkToY == 0) {
+ goTo(ent->_posX, ent->_posY);
+ } else {
+ goTo(ent->_walkToX, ent->_walkToY, ent->_walkToDir);
+ }
+ if (strcmp(name, "GoToObjectAsync") != 0) {
+ script->waitForExclusive(this);
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TurnTo / TurnToAsync
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TurnTo") == 0 || strcmp(name, "TurnToAsync") == 0) {
+ stack->correctParams(1);
+ int dir;
+ ScValue *val = stack->pop();
+
+ // turn to object?
+ if (val->isNative() && _gameRef->validObject((BaseObject *)val->getNative())) {
+ BaseObject *obj = (BaseObject *)val->getNative();
+ int angle = (int)(atan2((double)(obj->_posY - _posY), (double)(obj->_posX - _posX)) * (180 / 3.14));
+ dir = (int)angleToDirection(angle);
+ }
+ // otherwise turn to direction
+ else {
+ dir = val->getInt();
+ }
+
+ if (dir >= 0 && dir < NUM_DIRECTIONS) {
+ turnTo((TDirection)dir);
+ if (strcmp(name, "TurnToAsync") != 0) {
+ script->waitForExclusive(this);
+ }
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsWalking
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsWalking") == 0) {
+ stack->correctParams(0);
+ stack->pushBool(_state == STATE_FOLLOWING_PATH);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MergeAnims
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MergeAnims") == 0) {
+ stack->correctParams(1);
+ stack->pushBool(DID_SUCCEED(mergeAnims(stack->pop()->getString())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // UnloadAnim
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "UnloadAnim") == 0) {
+ stack->correctParams(1);
+ const char *animName = stack->pop()->getString();
+
+ bool found = false;
+ for (uint32 i = 0; i < _anims.size(); i++) {
+ if (scumm_stricmp(_anims[i]->getName(), animName) == 0) {
+ // invalidate sprites in use
+ if (_anims[i]->containsSprite(_tempSprite2)) {
+ _tempSprite2 = NULL;
+ }
+ if (_anims[i]->containsSprite(_currentSprite)) {
+ _currentSprite = NULL;
+ }
+ if (_anims[i]->containsSprite(_animSprite2)) {
+ _animSprite2 = NULL;
+ }
+
+ delete _anims[i];
+ _anims[i] = NULL;
+ _anims.remove_at(i);
+ i--;
+ found = true;
+ }
+ }
+ stack->pushBool(found);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HasAnim
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HasAnim") == 0) {
+ stack->correctParams(1);
+ const char *animName = stack->pop()->getString();
+ stack->pushBool(getAnimByName(animName) != NULL);
+ return STATUS_OK;
+ } else {
+ return AdTalkHolder::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdActor::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Direction
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Direction") {
+ _scValue->setInt(_dir);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Type") {
+ _scValue->setString("actor");
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // TalkAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TalkAnimName") {
+ _scValue->setString(_talkAnimName);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WalkAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "WalkAnimName") {
+ _scValue->setString(_walkAnimName);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IdleAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "IdleAnimName") {
+ _scValue->setString(_idleAnimName);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TurnLeftAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TurnLeftAnimName") {
+ _scValue->setString(_turnLeftAnimName);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TurnRightAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TurnRightAnimName") {
+ _scValue->setString(_turnRightAnimName);
+ return _scValue;
+ } else {
+ return AdTalkHolder::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Direction
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Direction") == 0) {
+ int dir = value->getInt();
+ if (dir >= 0 && dir < NUM_DIRECTIONS) {
+ _dir = (TDirection)dir;
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TalkAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TalkAnimName") == 0) {
+ if (value->isNULL()) {
+ _talkAnimName = "talk";
+ } else {
+ _talkAnimName = value->getString();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WalkAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WalkAnimName") == 0) {
+ if (value->isNULL()) {
+ _walkAnimName = "walk";
+ } else {
+ _walkAnimName = value->getString();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IdleAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IdleAnimName") == 0) {
+ if (value->isNULL()) {
+ _idleAnimName = "idle";
+ } else {
+ _idleAnimName = value->getString();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TurnLeftAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TurnLeftAnimName") == 0) {
+ if (value->isNULL()) {
+ _turnLeftAnimName = "turnleft";
+ } else {
+ _turnLeftAnimName = value->getString();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TurnRightAnimName
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TurnRightAnimName") == 0) {
+ if (value->isNULL()) {
+ _turnRightAnimName = "turnright";
+ } else {
+ _turnRightAnimName = value->getString();
+ }
+ return STATUS_OK;
+ } else {
+ return AdTalkHolder::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdActor::scToString() {
+ return "[actor object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSprite *AdActor::getTalkStance(const char *stance) {
+ // forced stance?
+ if (_forcedTalkAnimName && !_forcedTalkAnimUsed) {
+ _forcedTalkAnimUsed = true;
+ delete _animSprite;
+ _animSprite = new BaseSprite(_gameRef, this);
+ if (_animSprite) {
+ bool res = _animSprite->loadFile(_forcedTalkAnimName);
+ if (DID_FAIL(res)) {
+ _gameRef->LOG(res, "AdActor::GetTalkStance: error loading talk sprite (object:\"%s\" sprite:\"%s\")", getName(), _forcedTalkAnimName);
+ delete _animSprite;
+ _animSprite = NULL;
+ } else {
+ return _animSprite;
+ }
+ }
+ }
+
+ // old way
+ if (_talkSprites.size() > 0 || _talkSpritesEx.size() > 0) {
+ return getTalkStanceOld(stance);
+ }
+
+ // new way
+ BaseSprite *ret = NULL;
+
+ // do we have an animation with this name?
+ AdSpriteSet *anim = getAnimByName(stance);
+ if (anim) {
+ ret = anim->getSprite(_dir);
+ }
+
+ // not - get a random talk
+ if (!ret) {
+ BaseArray<AdSpriteSet *> talkAnims;
+ for (uint32 i = 0; i < _anims.size(); i++) {
+ if (_talkAnimName.compareToIgnoreCase(_anims[i]->getName()) == 0) {
+ talkAnims.add(_anims[i]);
+ }
+ }
+
+ if (talkAnims.size() > 0) {
+ int rnd = BaseEngine::instance().randInt(0, talkAnims.size() - 1);
+ ret = talkAnims[rnd]->getSprite(_dir);
+ } else {
+ if (_standSprite) {
+ ret = _standSprite->getSprite(_dir);
+ } else {
+ anim = getAnimByName(_idleAnimName);
+ if (anim) {
+ ret = anim->getSprite(_dir);
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseSprite *AdActor::getTalkStanceOld(const char *stance) {
+ BaseSprite *ret = NULL;
+
+ if (stance != NULL) {
+ // search special stances
+ for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
+ if (scumm_stricmp(_talkSpritesEx[i]->getName(), stance) == 0) {
+ ret = _talkSpritesEx[i]->getSprite(_dir);
+ break;
+ }
+ }
+ if (ret == NULL) {
+ // search generic stances
+ for (uint32 i = 0; i < _talkSprites.size(); i++) {
+ if (scumm_stricmp(_talkSprites[i]->getName(), stance) == 0) {
+ ret = _talkSprites[i]->getSprite(_dir);
+ break;
+ }
+ }
+ }
+ }
+
+ // not a valid stance? get a random one
+ if (ret == NULL) {
+ if (_talkSprites.size() < 1) {
+ ret = _standSprite->getSprite(_dir);
+ } else {
+ // TODO: remember last
+ int rnd = BaseEngine::instance().randInt(0, _talkSprites.size() - 1);
+ ret = _talkSprites[rnd]->getSprite(_dir);
+ }
+ }
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::persist(BasePersistenceManager *persistMgr) {
+ AdTalkHolder::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER_INT(_dir));
+ persistMgr->transfer(TMEMBER(_path));
+ persistMgr->transfer(TMEMBER(_pFCount));
+ persistMgr->transfer(TMEMBER(_pFStepX));
+ persistMgr->transfer(TMEMBER(_pFStepY));
+ persistMgr->transfer(TMEMBER(_pFX));
+ persistMgr->transfer(TMEMBER(_pFY));
+ persistMgr->transfer(TMEMBER(_standSprite));
+ _talkSprites.persist(persistMgr);
+ _talkSpritesEx.persist(persistMgr);
+ persistMgr->transfer(TMEMBER_INT(_targetDir));
+ persistMgr->transfer(TMEMBER_INT(_afterWalkDir));
+ persistMgr->transfer(TMEMBER(_targetPoint));
+ persistMgr->transfer(TMEMBER(_turnLeftSprite));
+ persistMgr->transfer(TMEMBER(_turnRightSprite));
+ persistMgr->transfer(TMEMBER(_walkSprite));
+
+ persistMgr->transfer(TMEMBER(_animSprite2));
+ persistMgr->transfer(TMEMBER(_talkAnimName));
+ persistMgr->transfer(TMEMBER(_idleAnimName));
+ persistMgr->transfer(TMEMBER(_walkAnimName));
+ persistMgr->transfer(TMEMBER(_turnLeftAnimName));
+ persistMgr->transfer(TMEMBER(_turnRightAnimName));
+
+ _anims.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TDirection AdActor::angleToDirection(int angle) {
+ TDirection ret = DI_DOWN;
+
+ if (angle > -112 && angle <= -67) {
+ ret = DI_UP;
+ } else if (angle > -67 && angle <= -22) {
+ ret = DI_UPRIGHT;
+ } else if (angle > -22 && angle <= 22) {
+ ret = DI_RIGHT;
+ } else if (angle > 22 && angle <= 67) {
+ ret = DI_DOWNRIGHT;
+ } else if (angle > 67 && angle <= 112) {
+ ret = DI_DOWN;
+ } else if (angle > 112 && angle <= 157) {
+ ret = DI_DOWNLEFT;
+ } else if ((angle > 157 && angle <= 180) || (angle >= -180 && angle <= -157)) {
+ ret = DI_LEFT;
+ } else if (angle > -157 && angle <= -112) {
+ ret = DI_UPLEFT;
+ }
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int AdActor::getHeight() {
+ // if no current sprite is set, set some
+ if (_currentSprite == NULL) {
+ if (_standSprite) {
+ _currentSprite = _standSprite->getSprite(_dir);
+ } else {
+ AdSpriteSet *anim = getAnimByName(_idleAnimName);
+ if (anim) {
+ _currentSprite = anim->getSprite(_dir);
+ }
+ }
+ }
+ // and get height
+ return AdTalkHolder::getHeight();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdSpriteSet *AdActor::getAnimByName(const Common::String &animName) {
+ for (uint32 i = 0; i < _anims.size(); i++) {
+ if (animName.compareToIgnoreCase(_anims[i]->getName()) == 0) {
+ return _anims[i];
+ }
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::mergeAnims(const char *animsFilename) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ANIMATION)
+ TOKEN_TABLE_END
+
+
+ byte *fileBuffer = BaseFileManager::getEngineInstance()->readWholeFile(animsFilename);
+ if (fileBuffer == NULL) {
+ _gameRef->LOG(0, "AdActor::MergeAnims failed for file '%s'", animsFilename);
+ return STATUS_FAILED;
+ }
+
+ byte *buffer = fileBuffer;
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ bool ret = STATUS_OK;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_ANIMATION: {
+ AdSpriteSet *anim = new AdSpriteSet(_gameRef, this);
+ if (!anim || DID_FAIL(anim->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ ret = STATUS_FAILED;
+ } else {
+ _anims.add(anim);
+ }
+ }
+ break;
+ }
+ }
+ delete[] fileBuffer;
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdActor::playAnim(const char *filename) {
+ // if we have an anim with this name, use it
+ AdSpriteSet *anim = getAnimByName(filename);
+ if (anim) {
+ _animSprite2 = anim->getSprite(_dir);
+ if (_animSprite2) {
+ _animSprite2->reset();
+ _state = STATE_PLAYING_ANIM_SET;
+ return STATUS_OK;
+ }
+ }
+ // otherwise call the standard handler
+ return AdTalkHolder::playAnim(filename);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_actor.h b/engines/wintermute/ad/ad_actor.h
new file mode 100644
index 0000000000..543c9d063a
--- /dev/null
+++ b/engines/wintermute/ad/ad_actor.h
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADACTOR_H
+#define WINTERMUTE_ADACTOR_H
+
+
+#include "engines/wintermute/dctypes.h" // Added by ClassView
+#include "engines/wintermute/ad/ad_types.h" // Added by ClassView
+#include "engines/wintermute/ad/ad_talk_holder.h"
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/base/base_point.h" // Added by ClassView
+#include "engines/wintermute/persistent.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+class AdSpriteSet;
+class AdPath;
+class BaseSprite;
+class AdActor : public AdTalkHolder {
+public:
+ TDirection angleToDirection(int angle);
+ DECLARE_PERSISTENT(AdActor, AdTalkHolder)
+ virtual int getHeight();
+ BaseSprite *getTalkStance(const char *stance);
+ virtual void goTo(int x, int y, TDirection afterWalkDir = DI_NONE);
+ BasePoint *_targetPoint;
+ virtual bool update();
+ virtual bool display();
+ virtual void turnTo(TDirection dir);
+ AdActor(BaseGame *inGame/*=NULL*/);
+ virtual ~AdActor();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+
+
+private:
+ TDirection _targetDir;
+ TDirection _afterWalkDir;
+
+ AdPath *_path;
+ AdSpriteSet *_walkSprite;
+ AdSpriteSet *_standSprite;
+ AdSpriteSet *_turnLeftSprite;
+ AdSpriteSet *_turnRightSprite;
+ BaseArray<AdSpriteSet *> _talkSprites;
+ BaseArray<AdSpriteSet *> _talkSpritesEx;
+ TDirection _dir;
+ // new anim system
+ Common::String _talkAnimName;
+ Common::String _idleAnimName;
+ Common::String _walkAnimName;
+ Common::String _turnLeftAnimName;
+ Common::String _turnRightAnimName;
+ BaseArray<AdSpriteSet *> _anims;
+ virtual bool playAnim(const char *filename);
+ AdSpriteSet *getAnimByName(const Common::String &animName);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+
+ bool setDefaultAnimNames();
+ BaseSprite *getTalkStanceOld(const char *stance);
+ bool mergeAnims(const char *animsFilename);
+ BaseSprite *_animSprite2;
+
+ void initLine(BasePoint startPt, BasePoint endPt);
+ void getNextStep();
+ void followPath();
+ double _pFStepX;
+ double _pFStepY;
+ double _pFX;
+ double _pFY;
+ int _pFCount;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_entity.cpp b/engines/wintermute/ad/ad_entity.cpp
new file mode 100644
index 0000000000..9af7e034ca
--- /dev/null
+++ b/engines/wintermute/ad/ad_entity.cpp
@@ -0,0 +1,1122 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+
+#include "engines/wintermute/ad/ad_entity.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/ad/ad_waypoint_group.h"
+#include "engines/wintermute/ad/ad_sentence.h"
+#include "engines/wintermute/base/base_active_rect.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_surface_storage.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/particles/part_emitter.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/video/video_theora_player.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdEntity, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdEntity::AdEntity(BaseGame *inGame) : AdTalkHolder(inGame) {
+ _type = OBJECT_ENTITY;
+ _subtype = ENTITY_NORMAL;
+ _region = NULL;
+ _item = NULL;
+
+ _walkToX = _walkToY = 0;
+ _walkToDir = DI_NONE;
+
+ _theora = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdEntity::~AdEntity() {
+ _gameRef->unregisterObject(_region);
+
+ delete _theora;
+ _theora = NULL;
+
+ delete[] _item;
+ _item = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdEntity::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing ENTITY file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(ENTITY)
+TOKEN_DEF(SPRITE)
+TOKEN_DEF(X)
+TOKEN_DEF(Y)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(NAME)
+TOKEN_DEF(SCALABLE)
+TOKEN_DEF(REGISTRABLE)
+TOKEN_DEF(INTERACTIVE)
+TOKEN_DEF(SHADOWABLE)
+TOKEN_DEF(COLORABLE)
+TOKEN_DEF(ACTIVE)
+TOKEN_DEF(EVENTS)
+TOKEN_DEF(FONT)
+TOKEN_DEF(TALK_SPECIAL)
+TOKEN_DEF(TALK)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(REGION)
+TOKEN_DEF(BLOCKED_REGION)
+TOKEN_DEF(EDITOR_SELECTED)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(SOUND_START_TIME)
+TOKEN_DEF(SOUND_VOLUME)
+TOKEN_DEF(SOUND_PANNING)
+TOKEN_DEF(SOUND)
+TOKEN_DEF(SUBTYPE)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(WAYPOINTS)
+TOKEN_DEF(IGNORE_ITEMS)
+TOKEN_DEF(ROTABLE)
+TOKEN_DEF(ROTATABLE)
+TOKEN_DEF(ALPHA_COLOR)
+TOKEN_DEF(SCALE)
+TOKEN_DEF(RELATIVE_SCALE)
+TOKEN_DEF(ALPHA)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(ITEM)
+TOKEN_DEF(WALK_TO_X)
+TOKEN_DEF(WALK_TO_Y)
+TOKEN_DEF(WALK_TO_DIR)
+TOKEN_DEF(SAVE_STATE)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ENTITY)
+ TOKEN_TABLE(SPRITE)
+ TOKEN_TABLE(X)
+ TOKEN_TABLE(Y)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(SCALABLE)
+ TOKEN_TABLE(REGISTRABLE)
+ TOKEN_TABLE(INTERACTIVE)
+ TOKEN_TABLE(SHADOWABLE)
+ TOKEN_TABLE(COLORABLE)
+ TOKEN_TABLE(ACTIVE)
+ TOKEN_TABLE(EVENTS)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(TALK_SPECIAL)
+ TOKEN_TABLE(TALK)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(REGION)
+ TOKEN_TABLE(BLOCKED_REGION)
+ TOKEN_TABLE(EDITOR_SELECTED)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(SOUND_START_TIME)
+ TOKEN_TABLE(SOUND_VOLUME)
+ TOKEN_TABLE(SOUND_PANNING)
+ TOKEN_TABLE(SOUND)
+ TOKEN_TABLE(SUBTYPE)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(WAYPOINTS)
+ TOKEN_TABLE(IGNORE_ITEMS)
+ TOKEN_TABLE(ROTABLE)
+ TOKEN_TABLE(ROTATABLE)
+ TOKEN_TABLE(ALPHA_COLOR)
+ TOKEN_TABLE(SCALE)
+ TOKEN_TABLE(RELATIVE_SCALE)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(ITEM)
+ TOKEN_TABLE(WALK_TO_X)
+ TOKEN_TABLE(WALK_TO_Y)
+ TOKEN_TABLE(WALK_TO_DIR)
+ TOKEN_TABLE(SAVE_STATE)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_ENTITY) {
+ _gameRef->LOG(0, "'ENTITY' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ AdGame *adGame = (AdGame *)_gameRef;
+ BaseSprite *spr = NULL;
+ int ar = 0, ag = 0, ab = 0, alpha = 0;
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_X:
+ parser.scanStr((char *)params, "%d", &_posX);
+ break;
+
+ case TOKEN_Y:
+ parser.scanStr((char *)params, "%d", &_posY);
+ break;
+
+ case TOKEN_SPRITE: {
+ delete _sprite;
+ _sprite = NULL;
+ spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprite = spr;
+ }
+ }
+ break;
+
+ case TOKEN_TALK: {
+ spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, adGame->_texTalkLifeTime))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _talkSprites.add(spr);
+ }
+ }
+ break;
+
+ case TOKEN_TALK_SPECIAL: {
+ spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, adGame->_texTalkLifeTime))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _talkSpritesEx.add(spr);
+ }
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_ITEM:
+ setItem((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_FONT:
+ setFont((char *)params);
+ break;
+
+ case TOKEN_SCALABLE:
+ parser.scanStr((char *)params, "%b", &_zoomable);
+ break;
+
+ case TOKEN_SCALE: {
+ int s;
+ parser.scanStr((char *)params, "%d", &s);
+ _scale = (float)s;
+
+ }
+ break;
+
+ case TOKEN_RELATIVE_SCALE: {
+ int s;
+ parser.scanStr((char *)params, "%d", &s);
+ _relativeScale = (float)s;
+
+ }
+ break;
+
+ case TOKEN_ROTABLE:
+ case TOKEN_ROTATABLE:
+ parser.scanStr((char *)params, "%b", &_rotatable);
+ break;
+
+ case TOKEN_REGISTRABLE:
+ case TOKEN_INTERACTIVE:
+ parser.scanStr((char *)params, "%b", &_registrable);
+ break;
+
+ case TOKEN_SHADOWABLE:
+ case TOKEN_COLORABLE:
+ parser.scanStr((char *)params, "%b", &_shadowable);
+ break;
+
+ case TOKEN_ACTIVE:
+ parser.scanStr((char *)params, "%b", &_active);
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_EDITOR_SELECTED:
+ parser.scanStr((char *)params, "%b", &_editorSelected);
+ break;
+
+ case TOKEN_REGION: {
+ if (_region) {
+ _gameRef->unregisterObject(_region);
+ }
+ _region = NULL;
+ BaseRegion *rgn = new BaseRegion(_gameRef);
+ if (!rgn || DID_FAIL(rgn->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _region = rgn;
+ _gameRef->registerObject(_region);
+ }
+ }
+ break;
+
+ case TOKEN_BLOCKED_REGION: {
+ delete _blockRegion;
+ _blockRegion = NULL;
+ delete _currentBlockRegion;
+ _currentBlockRegion = NULL;
+ BaseRegion *rgn = new BaseRegion(_gameRef);
+ BaseRegion *crgn = new BaseRegion(_gameRef);
+ if (!rgn || !crgn || DID_FAIL(rgn->loadBuffer(params, false))) {
+ delete _blockRegion;
+ _blockRegion = NULL;
+ delete _currentBlockRegion;
+ _currentBlockRegion = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ _blockRegion = rgn;
+ _currentBlockRegion = crgn;
+ _currentBlockRegion->mimic(_blockRegion);
+ }
+ }
+ break;
+
+ case TOKEN_WAYPOINTS: {
+ delete _wptGroup;
+ _wptGroup = NULL;
+ delete _currentWptGroup;
+ _currentWptGroup = NULL;
+ AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef);
+ AdWaypointGroup *cwpt = new AdWaypointGroup(_gameRef);
+ if (!wpt || !cwpt || DID_FAIL(wpt->loadBuffer(params, false))) {
+ delete _wptGroup;
+ _wptGroup = NULL;
+ delete _currentWptGroup;
+ _currentWptGroup = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ _wptGroup = wpt;
+ _currentWptGroup = cwpt;
+ _currentWptGroup->mimic(_wptGroup);
+ }
+ }
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_SUBTYPE: {
+ if (scumm_stricmp((char *)params, "sound") == 0) {
+ delete _sprite;
+ _sprite = NULL;
+ if (_gameRef->_editorMode) {
+ spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile("entity_sound.sprite"))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprite = spr;
+ }
+ }
+ if (_gameRef->_editorMode) {
+ _editorOnly = true;
+ }
+ _zoomable = false;
+ _rotatable = false;
+ _registrable = _gameRef->_editorMode;
+ _shadowable = false;
+ _subtype = ENTITY_SOUND;
+ }
+ }
+ break;
+
+ case TOKEN_SOUND:
+ playSFX((char *)params, false, false);
+ break;
+
+ case TOKEN_SOUND_START_TIME:
+ parser.scanStr((char *)params, "%d", &_sFXStart);
+ break;
+
+ case TOKEN_SOUND_VOLUME:
+ parser.scanStr((char *)params, "%d", &_sFXVolume);
+ break;
+
+ case TOKEN_SOUND_PANNING:
+ parser.scanStr((char *)params, "%b", &_autoSoundPanning);
+ break;
+
+ case TOKEN_SAVE_STATE:
+ parser.scanStr((char *)params, "%b", &_saveState);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+
+ case TOKEN_IGNORE_ITEMS:
+ parser.scanStr((char *)params, "%b", &_ignoreItems);
+ break;
+
+ case TOKEN_ALPHA_COLOR:
+ parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab);
+ break;
+
+ case TOKEN_ALPHA:
+ parser.scanStr((char *)params, "%d", &alpha);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+
+ case TOKEN_WALK_TO_X:
+ parser.scanStr((char *)params, "%d", &_walkToX);
+ break;
+
+ case TOKEN_WALK_TO_Y:
+ parser.scanStr((char *)params, "%d", &_walkToY);
+ break;
+
+ case TOKEN_WALK_TO_DIR: {
+ int i;
+ parser.scanStr((char *)params, "%d", &i);
+ if (i < 0) {
+ i = 0;
+ }
+ if (i >= NUM_DIRECTIONS) {
+ i = DI_NONE;
+ }
+ _walkToDir = (TDirection)i;
+ }
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in ENTITY definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading ENTITY definition");
+ if (spr) {
+ delete spr;
+ }
+ return STATUS_FAILED;
+ }
+
+ if (_region && _sprite) {
+ _gameRef->LOG(0, "Warning: Entity '%s' has both sprite and region.", getName());
+ }
+
+ updatePosition();
+
+ if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) {
+ ar = ag = ab = 255;
+ }
+ _alphaColor = BYTETORGBA(ar, ag, ab, alpha);
+ _state = STATE_READY;
+
+ if (_item && ((AdGame *)_gameRef)->isItemTaken(_item)) {
+ _active = false;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::display() {
+ if (_active) {
+ updateSounds();
+
+ uint32 alpha;
+ if (_alphaColor != 0) {
+ alpha = _alphaColor;
+ } else {
+ alpha = _shadowable ? ((AdGame *)_gameRef)->_scene->getAlphaAt(_posX, _posY) : 0xFFFFFFFF;
+ }
+
+ float scaleX, scaleY;
+ getScale(&scaleX, &scaleY);
+
+ float rotate;
+ if (_rotatable) {
+ if (_rotateValid) {
+ rotate = _rotate;
+ } else {
+ rotate = ((AdGame *)_gameRef)->_scene->getRotationAt(_posX, _posY) + _relativeRotate;
+ }
+ } else {
+ rotate = 0.0f;
+ }
+
+
+ bool reg = _registrable;
+ if (_ignoreItems && ((AdGame *)_gameRef)->_selectedItem) {
+ reg = false;
+ }
+
+ if (_region && (reg || _editorAlwaysRegister)) {
+ _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, _registerAlias, _region, _gameRef->_offsetX, _gameRef->_offsetY));
+ }
+
+ displaySpriteAttachments(true);
+ if (_theora && (_theora->isPlaying() || _theora->isPaused())) {
+ _theora->display(alpha);
+ } else if (_currentSprite) {
+ _currentSprite->display(_posX,
+ _posY,
+ (reg || _editorAlwaysRegister) ? _registerAlias : NULL,
+ scaleX,
+ scaleY,
+ alpha,
+ rotate,
+ _blendMode);
+ }
+ displaySpriteAttachments(false);
+
+ if (_partEmitter) {
+ _partEmitter->display(_region);
+ }
+
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::update() {
+ _currentSprite = NULL;
+
+ if (_state == STATE_READY && _animSprite) {
+ delete _animSprite;
+ _animSprite = NULL;
+ }
+
+ // finished playing animation?
+ if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) {
+ _state = STATE_READY;
+ _currentSprite = _animSprite;
+ }
+
+ if (_sentence && _state != STATE_TALKING) {
+ _sentence->finish();
+ }
+
+ // default: stand animation
+ if (!_currentSprite) {
+ _currentSprite = _sprite;
+ }
+
+ switch (_state) {
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_PLAYING_ANIM:
+ _currentSprite = _animSprite;
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_READY:
+ if (!_animSprite) {
+ _currentSprite = _sprite;
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_TALKING: {
+ _sentence->update();
+ if (_sentence->_currentSprite) {
+ _tempSprite2 = _sentence->_currentSprite;
+ }
+
+ bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime);
+ if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) {
+ if (timeIsUp) {
+ _sentence->finish();
+ _tempSprite2 = NULL;
+ _state = STATE_READY;
+ } else {
+ _tempSprite2 = getTalkStance(_sentence->getNextStance());
+ if (_tempSprite2) {
+ _tempSprite2->reset();
+ _currentSprite = _tempSprite2;
+ }
+ ((AdGame *)_gameRef)->addSentence(_sentence);
+ }
+ } else {
+ _currentSprite = _tempSprite2;
+ ((AdGame *)_gameRef)->addSentence(_sentence);
+ }
+ }
+ break;
+ default: // Silence unhandled enum-warning
+ break;
+ }
+
+
+ if (_currentSprite) {
+ _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100);
+ if (_currentSprite->isChanged()) {
+ _posX += _currentSprite->_moveX;
+ _posY += _currentSprite->_moveY;
+ }
+ }
+
+ updateBlockRegion();
+ _ready = (_state == STATE_READY);
+
+ if (_theora) {
+ int offsetX, offsetY;
+ _gameRef->getOffset(&offsetX, &offsetY);
+ _theora->_posX = _posX - offsetX;
+ _theora->_posY = _posY - offsetY;
+
+ _theora->update();
+ if (_theora->isFinished()) {
+ _theora->stop();
+ delete _theora;
+ _theora = NULL;
+ }
+ }
+
+ updatePartEmitter();
+ updateSpriteAttachments();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // StopSound
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "StopSound") == 0 && _subtype == ENTITY_SOUND) {
+ stack->correctParams(0);
+
+ if (DID_FAIL(stopSFX(false))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PlayTheora
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PlayTheora") == 0) {
+ stack->correctParams(4);
+ const char *filename = stack->pop()->getString();
+ bool looping = stack->pop()->getBool(false);
+ ScValue *valAlpha = stack->pop();
+ int startTime = stack->pop()->getInt();
+
+ delete _theora;
+ _theora = new VideoTheoraPlayer(_gameRef);
+ if (_theora && DID_SUCCEED(_theora->initialize(filename))) {
+ if (!valAlpha->isNULL()) {
+ _theora->setAlphaImage(valAlpha->getString());
+ }
+ _theora->play(VID_PLAY_POS, 0, 0, false, false, looping, startTime, _scale >= 0.0f ? _scale : -1.0f, _sFXVolume);
+ //if (_scale>=0) _theora->_playZoom = _scale;
+ stack->pushBool(true);
+ } else {
+ script->runtimeError("Entity.PlayTheora - error playing video '%s'", filename);
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // StopTheora
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "StopTheora") == 0) {
+ stack->correctParams(0);
+ if (_theora) {
+ _theora->stop();
+ delete _theora;
+ _theora = NULL;
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsTheoraPlaying
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsTheoraPlaying") == 0) {
+ stack->correctParams(0);
+ if (_theora && _theora->isPlaying()) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PauseTheora
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PauseTheora") == 0) {
+ stack->correctParams(0);
+ if (_theora && _theora->isPlaying()) {
+ _theora->pause();
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ResumeTheora
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ResumeTheora") == 0) {
+ stack->correctParams(0);
+ if (_theora && _theora->isPaused()) {
+ _theora->resume();
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsTheoraPaused
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsTheoraPaused") == 0) {
+ stack->correctParams(0);
+ if (_theora && _theora->isPaused()) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateRegion
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateRegion") == 0) {
+ stack->correctParams(0);
+ if (!_region) {
+ _region = new BaseRegion(_gameRef);
+ _gameRef->registerObject(_region);
+ }
+ if (_region) {
+ stack->pushNative(_region, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteRegion
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteRegion") == 0) {
+ stack->correctParams(0);
+ if (_region) {
+ _gameRef->unregisterObject(_region);
+ _region = NULL;
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ } else {
+ return AdTalkHolder::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdEntity::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("entity");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Item
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Item") {
+ if (_item) {
+ _scValue->setString(_item);
+ } else {
+ _scValue->setNULL();
+ }
+
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Subtype (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Subtype") {
+ if (_subtype == ENTITY_SOUND) {
+ _scValue->setString("sound");
+ } else {
+ _scValue->setString("normal");
+ }
+
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WalkToX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "WalkToX") {
+ _scValue->setInt(_walkToX);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WalkToY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "WalkToY") {
+ _scValue->setInt(_walkToY);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WalkToDirection
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "WalkToDirection") {
+ _scValue->setInt((int)_walkToDir);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Region (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Region") {
+ if (_region) {
+ _scValue->setNative(_region, true);
+ } else {
+ _scValue->setNULL();
+ }
+ return _scValue;
+ } else {
+ return AdTalkHolder::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::scSetProperty(const char *name, ScValue *value) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Item
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Item") == 0) {
+ setItem(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WalkToX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WalkToX") == 0) {
+ _walkToX = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WalkToY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WalkToY") == 0) {
+ _walkToY = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WalkToDirection
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WalkToDirection") == 0) {
+ int dir = value->getInt();
+ if (dir >= 0 && dir < NUM_DIRECTIONS) {
+ _walkToDir = (TDirection)dir;
+ }
+ return STATUS_OK;
+ } else {
+ return AdTalkHolder::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdEntity::scToString() {
+ return "[entity object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "ENTITY {\n");
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ if (_subtype == ENTITY_SOUND) {
+ buffer->putTextIndent(indent + 2, "SUBTYPE=\"SOUND\"\n");
+ }
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+ buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "X=%d\n", _posX);
+ buffer->putTextIndent(indent + 2, "Y=%d\n", _posY);
+ buffer->putTextIndent(indent + 2, "SCALABLE=%s\n", _zoomable ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "INTERACTIVE=%s\n", _registrable ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "COLORABLE=%s\n", _shadowable ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE");
+ if (_ignoreItems) {
+ buffer->putTextIndent(indent + 2, "IGNORE_ITEMS=%s\n", _ignoreItems ? "TRUE" : "FALSE");
+ }
+ if (_rotatable) {
+ buffer->putTextIndent(indent + 2, "ROTATABLE=%s\n", _rotatable ? "TRUE" : "FALSE");
+ }
+
+ if (!_autoSoundPanning) {
+ buffer->putTextIndent(indent + 2, "SOUND_PANNING=%s\n", _autoSoundPanning ? "TRUE" : "FALSE");
+ }
+
+ if (!_saveState) {
+ buffer->putTextIndent(indent + 2, "SAVE_STATE=%s\n", _saveState ? "TRUE" : "FALSE");
+ }
+
+ if (_item && _item[0] != '\0') {
+ buffer->putTextIndent(indent + 2, "ITEM=\"%s\"\n", _item);
+ }
+
+ buffer->putTextIndent(indent + 2, "WALK_TO_X=%d\n", _walkToX);
+ buffer->putTextIndent(indent + 2, "WALK_TO_Y=%d\n", _walkToY);
+ if (_walkToDir != DI_NONE) {
+ buffer->putTextIndent(indent + 2, "WALK_TO_DIR=%d\n", (int)_walkToDir);
+ }
+
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ if (_subtype == ENTITY_NORMAL && _sprite && _sprite->getFilename()) {
+ buffer->putTextIndent(indent + 2, "SPRITE=\"%s\"\n", _sprite->getFilename());
+ }
+
+ if (_subtype == ENTITY_SOUND && _sFX && _sFX->getFilename()) {
+ buffer->putTextIndent(indent + 2, "SOUND=\"%s\"\n", _sFX->getFilename());
+ buffer->putTextIndent(indent + 2, "SOUND_START_TIME=%d\n", _sFXStart);
+ buffer->putTextIndent(indent + 2, "SOUND_VOLUME=%d\n", _sFXVolume);
+ }
+
+
+ if (RGBCOLGetR(_alphaColor) != 0 || RGBCOLGetG(_alphaColor) != 0 || RGBCOLGetB(_alphaColor) != 0) {
+ buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alphaColor), RGBCOLGetG(_alphaColor), RGBCOLGetB(_alphaColor));
+ }
+
+ if (RGBCOLGetA(_alphaColor) != 0) {
+ buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alphaColor));
+ }
+
+ if (_scale >= 0) {
+ buffer->putTextIndent(indent + 2, "SCALE = %d\n", (int)_scale);
+ }
+
+ if (_relativeScale != 0) {
+ buffer->putTextIndent(indent + 2, "RELATIVE_SCALE = %d\n", (int)_relativeScale);
+ }
+
+ if (_font && _font->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename());
+ }
+
+ if (_cursor && _cursor->getFilename()) {
+ buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename());
+ }
+
+ AdTalkHolder::saveAsText(buffer, indent + 2);
+
+ if (_region) {
+ _region->saveAsText(buffer, indent + 2);
+ }
+
+ if (_scProp) {
+ _scProp->saveAsText(buffer, indent + 2);
+ }
+
+ AdObject::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int AdEntity::getHeight() {
+ if (_region && !_sprite) {
+ return _region->_rect.bottom - _region->_rect.top;
+ } else {
+ if (_currentSprite == NULL) {
+ _currentSprite = _sprite;
+ }
+ return AdObject::getHeight();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdEntity::updatePosition() {
+ if (_region && !_sprite) {
+ _posX = _region->_rect.left + (_region->_rect.right - _region->_rect.left) / 2;
+ _posY = _region->_rect.bottom;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::persist(BasePersistenceManager *persistMgr) {
+ AdTalkHolder::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_item));
+ persistMgr->transfer(TMEMBER(_region));
+ //persistMgr->transfer(TMEMBER(_sprite));
+ persistMgr->transfer(TMEMBER_INT(_subtype));
+ _talkSprites.persist(persistMgr);
+ _talkSpritesEx.persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_walkToX));
+ persistMgr->transfer(TMEMBER(_walkToY));
+ persistMgr->transfer(TMEMBER_INT(_walkToDir));
+
+ persistMgr->transfer(TMEMBER(_theora));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdEntity::setItem(const char *itemName) {
+ BaseUtils::setString(&_item, itemName);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdEntity::setSprite(const char *filename) {
+ if (_currentSprite == _sprite) {
+ _currentSprite = NULL;
+ }
+
+ delete _sprite;
+ _sprite = NULL;
+ BaseSprite *spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile(filename))) {
+ delete _sprite;
+ _sprite = NULL;
+ return STATUS_FAILED;
+ } else {
+ _sprite = spr;
+ _currentSprite = _sprite;
+ return STATUS_OK;
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_entity.h b/engines/wintermute/ad/ad_entity.h
new file mode 100644
index 0000000000..415987e50a
--- /dev/null
+++ b/engines/wintermute/ad/ad_entity.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADENTITY_H
+#define WINTERMUTE_ADENTITY_H
+
+#include "engines/wintermute/ad/ad_talk_holder.h"
+
+namespace Wintermute {
+class VideoTheoraPlayer;
+class AdEntity : public AdTalkHolder {
+public:
+ VideoTheoraPlayer *_theora;
+ bool setSprite(const char *filename);
+ int _walkToX;
+ int _walkToY;
+ TDirection _walkToDir;
+ void setItem(const char *itemName);
+ char *_item;
+ DECLARE_PERSISTENT(AdEntity, AdTalkHolder)
+ void updatePosition();
+ virtual int getHeight();
+ BaseRegion *_region;
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ virtual bool update();
+ virtual bool display();
+ AdEntity(BaseGame *inGame);
+ virtual ~AdEntity();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ TEntityType _subtype;
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_game.cpp b/engines/wintermute/ad/ad_game.cpp
new file mode 100644
index 0000000000..4481b774c1
--- /dev/null
+++ b/engines/wintermute/ad/ad_game.cpp
@@ -0,0 +1,2281 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_actor.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_entity.h"
+#include "engines/wintermute/ad/ad_inventory.h"
+#include "engines/wintermute/ad/ad_inventory_box.h"
+#include "engines/wintermute/ad/ad_item.h"
+#include "engines/wintermute/ad/ad_response.h"
+#include "engines/wintermute/ad/ad_response_box.h"
+#include "engines/wintermute/ad/ad_response_context.h"
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/ad/ad_scene_state.h"
+#include "engines/wintermute/ad/ad_sentence.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/base_object.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/base_surface_storage.h"
+#include "engines/wintermute/base/base_transition_manager.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_viewport.h"
+#include "engines/wintermute/base/particles/part_emitter.h"
+#include "engines/wintermute/base/saveload.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script_engine.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/ui/ui_entity.h"
+#include "engines/wintermute/ui/ui_window.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/video/video_player.h"
+#include "engines/wintermute/video/video_theora_player.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdGame, true)
+
+//////////////////////////////////////////////////////////////////////////
+AdGame::AdGame(const Common::String &gameId) : BaseGame(gameId) {
+ _responseBox = NULL;
+ _inventoryBox = NULL;
+
+ _scene = new AdScene(_gameRef);
+ _scene->setName("");
+ registerObject(_scene);
+
+ _prevSceneName = NULL;
+ _prevSceneFilename = NULL;
+ _scheduledScene = NULL;
+ _scheduledFadeIn = false;
+
+
+ _stateEx = GAME_NORMAL;
+
+ _selectedItem = NULL;
+
+
+ _texItemLifeTime = 10000;
+ _texWalkLifeTime = 10000;
+ _texStandLifeTime = 10000;
+ _texTalkLifeTime = 10000;
+
+ _talkSkipButton = TALK_SKIP_LEFT;
+
+ _sceneViewport = NULL;
+
+ _initialScene = true;
+ _debugStartupScene = NULL;
+ _startupScene = NULL;
+
+ _invObject = new AdObject(this);
+ _inventoryOwner = _invObject;
+
+ _tempDisableSaveState = false;
+ _itemsFile = NULL;
+
+ _smartItemCursor = false;
+
+ addSpeechDir("speech");
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdGame::~AdGame() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::cleanup() {
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ unregisterObject(_objects[i]);
+ _objects[i] = NULL;
+ }
+ _objects.clear();
+
+
+ for (uint32 i = 0; i < _dlgPendingBranches.size(); i++) {
+ delete[] _dlgPendingBranches[i];
+ }
+ _dlgPendingBranches.clear();
+
+ for (uint32 i = 0; i < _speechDirs.size(); i++) {
+ delete[] _speechDirs[i];
+ }
+ _speechDirs.clear();
+
+
+ unregisterObject(_scene);
+ _scene = NULL;
+
+ // remove items
+ for (uint32 i = 0; i < _items.size(); i++) {
+ _gameRef->unregisterObject(_items[i]);
+ }
+ _items.clear();
+
+
+ // clear remaining inventories
+ delete _invObject;
+ _invObject = NULL;
+
+ for (uint32 i = 0; i < _inventories.size(); i++) {
+ delete _inventories[i];
+ }
+ _inventories.clear();
+
+
+ if (_responseBox) {
+ _gameRef->unregisterObject(_responseBox);
+ _responseBox = NULL;
+ }
+
+ if (_inventoryBox) {
+ _gameRef->unregisterObject(_inventoryBox);
+ _inventoryBox = NULL;
+ }
+
+ delete[] _prevSceneName;
+ delete[] _prevSceneFilename;
+ delete[] _scheduledScene;
+ delete[] _debugStartupScene;
+ delete[] _itemsFile;
+ _prevSceneName = NULL;
+ _prevSceneFilename = NULL;
+ _scheduledScene = NULL;
+ _debugStartupScene = NULL;
+ _startupScene = NULL;
+ _itemsFile = NULL;
+
+ delete _sceneViewport;
+ _sceneViewport = NULL;
+
+ for (uint32 i = 0; i < _sceneStates.size(); i++) {
+ delete _sceneStates[i];
+ }
+ _sceneStates.clear();
+
+ for (uint32 i = 0; i < _responsesBranch.size(); i++) {
+ delete _responsesBranch[i];
+ }
+ _responsesBranch.clear();
+
+ for (uint32 i = 0; i < _responsesGame.size(); i++) {
+ delete _responsesGame[i];
+ }
+ _responsesGame.clear();
+
+ return BaseGame::cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::initLoop() {
+ if (_scheduledScene && _transMgr->isReady()) {
+ changeScene(_scheduledScene, _scheduledFadeIn);
+ delete[] _scheduledScene;
+ _scheduledScene = NULL;
+
+ _gameRef->_activeObject = NULL;
+ }
+
+
+ bool res;
+ res = BaseGame::initLoop();
+ if (DID_FAIL(res)) {
+ return res;
+ }
+
+ if (_scene) {
+ res = _scene->initLoop();
+ }
+
+ _sentences.clear();
+
+ return res;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::addObject(AdObject *object) {
+ _objects.add(object);
+ return registerObject(object);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::removeObject(AdObject *object) {
+ // in case the user called Scene.CreateXXX() and Game.DeleteXXX()
+ if (_scene) {
+ bool res = _scene->removeObject(object);
+ if (DID_SUCCEED(res)) {
+ return res;
+ }
+ }
+
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i] == object) {
+ _objects.remove_at(i);
+ break;
+ }
+ }
+ return unregisterObject(object);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::changeScene(const char *filename, bool fadeIn) {
+ if (_scene == NULL) {
+ _scene = new AdScene(_gameRef);
+ registerObject(_scene);
+ } else {
+ _scene->applyEvent("SceneShutdown", true);
+
+ setPrevSceneName(_scene->getName());
+ setPrevSceneFilename(_scene->getFilename());
+
+ if (!_tempDisableSaveState) {
+ _scene->saveState();
+ }
+ _tempDisableSaveState = false;
+ }
+
+ if (_scene) {
+ // reset objects
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ _objects[i]->reset();
+ }
+
+ // reset scene properties
+ _scene->_sFXVolume = 100;
+ if (_scene->_scProp) {
+ _scene->_scProp->cleanup();
+ }
+
+ bool ret;
+ if (_initialScene && _debugDebugMode && _debugStartupScene) {
+ _initialScene = false;
+ ret = _scene->loadFile(_debugStartupScene);
+ } else {
+ ret = _scene->loadFile(filename);
+ }
+
+ if (DID_SUCCEED(ret)) {
+ // invalidate references to the original scene
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ _objects[i]->invalidateCurrRegions();
+ _objects[i]->_stickRegion = NULL;
+ }
+
+ _scene->loadState();
+ }
+ if (fadeIn) {
+ _gameRef->_transMgr->start(TRANSITION_FADE_IN);
+ }
+ return ret;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdGame::addSentence(AdSentence *sentence) {
+ _sentences.add(sentence);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::displaySentences(bool frozen) {
+ for (uint32 i = 0; i < _sentences.size(); i++) {
+ if (frozen && _sentences[i]->_freezable) {
+ continue;
+ } else {
+ _sentences[i]->display();
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdGame::finishSentences() {
+ for (uint32 i = 0; i < _sentences.size(); i++) {
+ if (_sentences[i]->canSkip()) {
+ _sentences[i]->_duration = 0;
+ if (_sentences[i]->_sound) {
+ _sentences[i]->_sound->stop();
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // ChangeScene
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "ChangeScene") == 0) {
+ stack->correctParams(3);
+ const char *filename = stack->pop()->getString();
+ ScValue *valFadeOut = stack->pop();
+ ScValue *valFadeIn = stack->pop();
+
+ bool transOut = valFadeOut->isNULL() ? true : valFadeOut->getBool();
+ bool transIn = valFadeIn->isNULL() ? true : valFadeIn->getBool();
+
+ scheduleChangeScene(filename, transIn);
+ if (transOut) {
+ _transMgr->start(TRANSITION_FADE_OUT, true);
+ }
+ stack->pushNULL();
+
+
+ //bool ret = ChangeScene(stack->pop()->getString());
+ //if (DID_FAIL(ret)) stack->pushBool(false);
+ //else stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadActor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadActor") == 0) {
+ stack->correctParams(1);
+ AdActor *act = new AdActor(_gameRef);
+ if (act && DID_SUCCEED(act->loadFile(stack->pop()->getString()))) {
+ addObject(act);
+ stack->pushNative(act, true);
+ } else {
+ delete act;
+ act = NULL;
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadEntity") == 0) {
+ stack->correctParams(1);
+ AdEntity *ent = new AdEntity(_gameRef);
+ if (ent && DID_SUCCEED(ent->loadFile(stack->pop()->getString()))) {
+ addObject(ent);
+ stack->pushNative(ent, true);
+ } else {
+ delete ent;
+ ent = NULL;
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // UnloadObject / UnloadActor / UnloadEntity / DeleteEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "UnloadObject") == 0 || strcmp(name, "UnloadActor") == 0 || strcmp(name, "UnloadEntity") == 0 || strcmp(name, "DeleteEntity") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ AdObject *obj = (AdObject *)val->getNative();
+ removeObject(obj);
+ if (val->getType() == VAL_VARIABLE_REF) {
+ val->setNULL();
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateEntity") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdEntity *ent = new AdEntity(_gameRef);
+ addObject(ent);
+ if (!val->isNULL()) {
+ ent->setName(val->getString());
+ }
+ stack->pushNative(ent, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateItem") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdItem *item = new AdItem(_gameRef);
+ addItem(item);
+ if (!val->isNULL()) {
+ item->setName(val->getString());
+ }
+ stack->pushNative(item, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteItem") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdItem *item = NULL;
+ if (val->isNative()) {
+ item = (AdItem *)val->getNative();
+ } else {
+ item = getItemByName(val->getString());
+ }
+
+ if (item) {
+ deleteItem(item);
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // QueryItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "QueryItem") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdItem *item = NULL;
+ if (val->isInt()) {
+ int index = val->getInt();
+ if (index >= 0 && index < (int32)_items.size()) {
+ item = _items[index];
+ }
+ } else {
+ item = getItemByName(val->getString());
+ }
+
+ if (item) {
+ stack->pushNative(item, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddResponse/AddResponseOnce/AddResponseOnceGame
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddResponse") == 0 || strcmp(name, "AddResponseOnce") == 0 || strcmp(name, "AddResponseOnceGame") == 0) {
+ stack->correctParams(6);
+ int id = stack->pop()->getInt();
+ const char *text = stack->pop()->getString();
+ ScValue *val1 = stack->pop();
+ ScValue *val2 = stack->pop();
+ ScValue *val3 = stack->pop();
+ ScValue *val4 = stack->pop();
+
+ if (_responseBox) {
+ AdResponse *res = new AdResponse(_gameRef);
+ if (res) {
+ res->_iD = id;
+ res->setText(text);
+ _stringTable->expand(&res->_text);
+ if (!val1->isNULL()) {
+ res->setIcon(val1->getString());
+ }
+ if (!val2->isNULL()) {
+ res->setIconHover(val2->getString());
+ }
+ if (!val3->isNULL()) {
+ res->setIconPressed(val3->getString());
+ }
+ if (!val4->isNULL()) {
+ res->setFont(val4->getString());
+ }
+
+ if (strcmp(name, "AddResponseOnce") == 0) {
+ res->_responseType = RESPONSE_ONCE;
+ } else if (strcmp(name, "AddResponseOnceGame") == 0) {
+ res->_responseType = RESPONSE_ONCE_GAME;
+ }
+
+ _responseBox->_responses.add(res);
+ }
+ } else {
+ script->runtimeError("Game.AddResponse: response box is not defined");
+ }
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ResetResponse
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ResetResponse") == 0) {
+ stack->correctParams(1);
+ int id = stack->pop()->getInt(-1);
+ resetResponse(id);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ClearResponses
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ClearResponses") == 0) {
+ stack->correctParams(0);
+ _responseBox->clearResponses();
+ _responseBox->clearButtons();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetResponse
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetResponse") == 0) {
+ stack->correctParams(1);
+ bool autoSelectLast = stack->pop()->getBool();
+
+ if (_responseBox) {
+ _responseBox->weedResponses();
+
+ if (_responseBox->_responses.size() == 0) {
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+
+ if (_responseBox->_responses.size() == 1 && autoSelectLast) {
+ stack->pushInt(_responseBox->_responses[0]->_iD);
+ _responseBox->handleResponse(_responseBox->_responses[0]);
+ _responseBox->clearResponses();
+ return STATUS_OK;
+ }
+
+ _responseBox->createButtons();
+ _responseBox->_waitingScript = script;
+ script->waitForExclusive(_responseBox);
+ _state = GAME_SEMI_FROZEN;
+ _stateEx = GAME_WAITING_RESPONSE;
+ } else {
+ script->runtimeError("Game.GetResponse: response box is not defined");
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetNumResponses
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetNumResponses") == 0) {
+ stack->correctParams(0);
+ if (_responseBox) {
+ _responseBox->weedResponses();
+ stack->pushInt(_responseBox->_responses.size());
+ } else {
+ script->runtimeError("Game.GetNumResponses: response box is not defined");
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // StartDlgBranch
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "StartDlgBranch") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ Common::String branchName;
+ if (val->isNULL()) {
+ branchName.format("line%d", script->_currentLine);
+ } else {
+ branchName = val->getString();
+ }
+
+ startDlgBranch(branchName.c_str(), script->_filename == NULL ? "" : script->_filename, script->_threadEvent == NULL ? "" : script->_threadEvent);
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // EndDlgBranch
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "EndDlgBranch") == 0) {
+ stack->correctParams(1);
+
+ const char *branchName = NULL;
+ ScValue *val = stack->pop();
+ if (!val->isNULL()) {
+ branchName = val->getString();
+ }
+ endDlgBranch(branchName, script->_filename == NULL ? "" : script->_filename, script->_threadEvent == NULL ? "" : script->_threadEvent);
+
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetCurrentDlgBranch
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetCurrentDlgBranch") == 0) {
+ stack->correctParams(0);
+
+ if (_dlgPendingBranches.size() > 0) {
+ stack->pushString(_dlgPendingBranches[_dlgPendingBranches.size() - 1]);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TakeItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TakeItem") == 0) {
+ return _invObject->scCallMethod(script, stack, thisStack, name);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DropItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DropItem") == 0) {
+ return _invObject->scCallMethod(script, stack, thisStack, name);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetItem") == 0) {
+ return _invObject->scCallMethod(script, stack, thisStack, name);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HasItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HasItem") == 0) {
+ return _invObject->scCallMethod(script, stack, thisStack, name);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsItemTaken
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsItemTaken") == 0) {
+ stack->correctParams(1);
+
+ ScValue *val = stack->pop();
+ if (!val->isNULL()) {
+ for (uint32 i = 0; i < _inventories.size(); i++) {
+ AdInventory *inv = _inventories[i];
+
+ for (uint32 j = 0; j < inv->_takenItems.size(); j++) {
+ if (val->getNative() == inv->_takenItems[j]) {
+ stack->pushBool(true);
+ return STATUS_OK;
+ } else if (scumm_stricmp(val->getString(), inv->_takenItems[j]->getName()) == 0) {
+ stack->pushBool(true);
+ return STATUS_OK;
+ }
+ }
+ }
+ } else {
+ script->runtimeError("Game.IsItemTaken: item name expected");
+ }
+
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetInventoryWindow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetInventoryWindow") == 0) {
+ stack->correctParams(0);
+ if (_inventoryBox && _inventoryBox->_window) {
+ stack->pushNative(_inventoryBox->_window, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetResponsesWindow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetResponsesWindow") == 0 || strcmp(name, "GetResponseWindow") == 0) {
+ stack->correctParams(0);
+ if (_responseBox && _responseBox->_window) {
+ stack->pushNative(_responseBox->_window, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadResponseBox
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadResponseBox") == 0) {
+ stack->correctParams(1);
+ const char *filename = stack->pop()->getString();
+
+ _gameRef->unregisterObject(_responseBox);
+ _responseBox = new AdResponseBox(_gameRef);
+ if (_responseBox && !DID_FAIL(_responseBox->loadFile(filename))) {
+ registerObject(_responseBox);
+ stack->pushBool(true);
+ } else {
+ delete _responseBox;
+ _responseBox = NULL;
+ stack->pushBool(false);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadInventoryBox
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadInventoryBox") == 0) {
+ stack->correctParams(1);
+ const char *filename = stack->pop()->getString();
+
+ _gameRef->unregisterObject(_inventoryBox);
+ _inventoryBox = new AdInventoryBox(_gameRef);
+ if (_inventoryBox && !DID_FAIL(_inventoryBox->loadFile(filename))) {
+ registerObject(_inventoryBox);
+ stack->pushBool(true);
+ } else {
+ delete _inventoryBox;
+ _inventoryBox = NULL;
+ stack->pushBool(false);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadItems
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadItems") == 0) {
+ stack->correctParams(2);
+ const char *filename = stack->pop()->getString();
+ bool merge = stack->pop()->getBool(false);
+
+ bool ret = loadItemsFile(filename, merge);
+ stack->pushBool(DID_SUCCEED(ret));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddSpeechDir
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddSpeechDir") == 0) {
+ stack->correctParams(1);
+ const char *dir = stack->pop()->getString();
+ stack->pushBool(DID_SUCCEED(addSpeechDir(dir)));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RemoveSpeechDir
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemoveSpeechDir") == 0) {
+ stack->correctParams(1);
+ const char *dir = stack->pop()->getString();
+ stack->pushBool(DID_SUCCEED(removeSpeechDir(dir)));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetSceneViewport
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetSceneViewport") == 0) {
+ stack->correctParams(4);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+ int width = stack->pop()->getInt();
+ int height = stack->pop()->getInt();
+
+ if (width <= 0) {
+ width = _renderer->_width;
+ }
+ if (height <= 0) {
+ height = _renderer->_height;
+ }
+
+ if (!_sceneViewport) {
+ _sceneViewport = new BaseViewport(_gameRef);
+ }
+ if (_sceneViewport) {
+ _sceneViewport->setRect(x, y, x + width, y + height);
+ }
+
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+
+ else {
+ return BaseGame::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdGame::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("game");
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Scene
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Scene") {
+ if (_scene) {
+ _scValue->setNative(_scene, true);
+ } else {
+ _scValue->setNULL();
+ }
+
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SelectedItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SelectedItem") {
+ //if (_selectedItem) _scValue->setString(_selectedItem->_name);
+ if (_selectedItem) {
+ _scValue->setNative(_selectedItem, true);
+ } else {
+ _scValue->setNULL();
+ }
+
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // NumItems
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumItems") {
+ return _invObject->scGetProperty(name);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SmartItemCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SmartItemCursor") {
+ _scValue->setBool(_smartItemCursor);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InventoryVisible
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "InventoryVisible") {
+ _scValue->setBool(_inventoryBox && _inventoryBox->_visible);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InventoryScrollOffset
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "InventoryScrollOffset") {
+ if (_inventoryBox) {
+ _scValue->setInt(_inventoryBox->_scrollOffset);
+ } else {
+ _scValue->setInt(0);
+ }
+
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ResponsesVisible (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ResponsesVisible") {
+ _scValue->setBool(_stateEx == GAME_WAITING_RESPONSE);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PrevScene / PreviousScene (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "PrevScene" || name == "PreviousScene") {
+ if (!_prevSceneName) {
+ _scValue->setString("");
+ } else {
+ _scValue->setString(_prevSceneName);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PrevSceneFilename / PreviousSceneFilename (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "PrevSceneFilename" || name == "PreviousSceneFilename") {
+ if (!_prevSceneFilename) {
+ _scValue->setString("");
+ } else {
+ _scValue->setString(_prevSceneFilename);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LastResponse (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "LastResponse") {
+ if (!_responseBox || !_responseBox->_lastResponseText) {
+ _scValue->setString("");
+ } else {
+ _scValue->setString(_responseBox->_lastResponseText);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LastResponseOrig (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "LastResponseOrig") {
+ if (!_responseBox || !_responseBox->_lastResponseTextOrig) {
+ _scValue->setString("");
+ } else {
+ _scValue->setString(_responseBox->_lastResponseTextOrig);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InventoryObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "InventoryObject") {
+ if (_inventoryOwner == _invObject) {
+ _scValue->setNative(this, true);
+ } else {
+ _scValue->setNative(_inventoryOwner, true);
+ }
+
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TotalNumItems
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TotalNumItems") {
+ _scValue->setInt(_items.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TalkSkipButton
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TalkSkipButton") {
+ _scValue->setInt(_talkSkipButton);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ChangingScene
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ChangingScene") {
+ _scValue->setBool(_scheduledScene != NULL);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // StartupScene
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "StartupScene") {
+ if (!_startupScene) {
+ _scValue->setNULL();
+ } else {
+ _scValue->setString(_startupScene);
+ }
+ return _scValue;
+ }
+
+ else {
+ return BaseGame::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::scSetProperty(const char *name, ScValue *value) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // SelectedItem
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SelectedItem") == 0) {
+ if (value->isNULL()) {
+ _selectedItem = NULL;
+ } else {
+ if (value->isNative()) {
+ _selectedItem = NULL;
+ for (uint32 i = 0; i < _items.size(); i++) {
+ if (_items[i] == value->getNative()) {
+ _selectedItem = (AdItem *)value->getNative();
+ break;
+ }
+ }
+ } else {
+ // try to get by name
+ _selectedItem = getItemByName(value->getString());
+ }
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SmartItemCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SmartItemCursor") == 0) {
+ _smartItemCursor = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InventoryVisible
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InventoryVisible") == 0) {
+ if (_inventoryBox) {
+ _inventoryBox->_visible = value->getBool();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InventoryObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InventoryObject") == 0) {
+ if (_inventoryOwner && _inventoryBox) {
+ _inventoryOwner->getInventory()->_scrollOffset = _inventoryBox->_scrollOffset;
+ }
+
+ if (value->isNULL()) {
+ _inventoryOwner = _invObject;
+ } else {
+ BaseObject *obj = (BaseObject *)value->getNative();
+ if (obj == this) {
+ _inventoryOwner = _invObject;
+ } else if (_gameRef->validObject(obj)) {
+ _inventoryOwner = (AdObject *)obj;
+ }
+ }
+
+ if (_inventoryOwner && _inventoryBox) {
+ _inventoryBox->_scrollOffset = _inventoryOwner->getInventory()->_scrollOffset;
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InventoryScrollOffset
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InventoryScrollOffset") == 0) {
+ if (_inventoryBox) {
+ _inventoryBox->_scrollOffset = value->getInt();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TalkSkipButton
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TalkSkipButton") == 0) {
+ int val = value->getInt();
+ if (val < 0) {
+ val = 0;
+ }
+ if (val > TALK_SKIP_NONE) {
+ val = TALK_SKIP_NONE;
+ }
+ _talkSkipButton = (TTalkSkipButton)val;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // StartupScene
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "StartupScene") == 0) {
+ if (value == NULL) {
+ delete[] _startupScene;
+ _startupScene = NULL;
+ } else {
+ BaseUtils::setString(&_startupScene, value->getString());
+ }
+
+ return STATUS_OK;
+ }
+
+ else {
+ return BaseGame::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name) {
+ ScValue *thisObj;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Actor
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Actor") == 0) {
+ stack->correctParams(0);
+ thisObj = thisStack->getTop();
+
+ thisObj->setNative(new AdActor(_gameRef));
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Entity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Entity") == 0) {
+ stack->correctParams(0);
+ thisObj = thisStack->getTop();
+
+ thisObj->setNative(new AdEntity(_gameRef));
+ stack->pushNULL();
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // call parent
+ else {
+ return BaseGame::externalCall(script, stack, thisStack, name);
+ }
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::showCursor() {
+ if (_cursorHidden) {
+ return STATUS_OK;
+ }
+
+ if (_selectedItem && _gameRef->_state == GAME_RUNNING && _stateEx == GAME_NORMAL && _interactive) {
+ if (_selectedItem->_cursorCombined) {
+ BaseSprite *origLastCursor = _lastCursor;
+ BaseGame::showCursor();
+ _lastCursor = origLastCursor;
+ }
+ if (_activeObject && _selectedItem->_cursorHover && _activeObject->getExtendedFlag("usable")) {
+ if (!_smartItemCursor || _activeObject->canHandleEvent(_selectedItem->getName())) {
+ return drawCursor(_selectedItem->_cursorHover);
+ } else {
+ return drawCursor(_selectedItem->_cursorNormal);
+ }
+ } else {
+ return drawCursor(_selectedItem->_cursorNormal);
+ }
+ } else {
+ return BaseGame::showCursor();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdGame::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing GAME file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(GAME)
+TOKEN_DEF(AD_GAME)
+TOKEN_DEF(RESPONSE_BOX)
+TOKEN_DEF(INVENTORY_BOX)
+TOKEN_DEF(ITEMS)
+TOKEN_DEF(ITEM)
+TOKEN_DEF(TALK_SKIP_BUTTON)
+TOKEN_DEF(SCENE_VIEWPORT)
+TOKEN_DEF(ENTITY_CONTAINER)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(STARTUP_SCENE)
+TOKEN_DEF(DEBUG_STARTUP_SCENE)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(GAME)
+ TOKEN_TABLE(AD_GAME)
+ TOKEN_TABLE(RESPONSE_BOX)
+ TOKEN_TABLE(INVENTORY_BOX)
+ TOKEN_TABLE(ITEMS)
+ TOKEN_TABLE(TALK_SKIP_BUTTON)
+ TOKEN_TABLE(SCENE_VIEWPORT)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(STARTUP_SCENE)
+ TOKEN_TABLE(DEBUG_STARTUP_SCENE)
+ TOKEN_TABLE_END
+
+ byte *params;
+ byte *params2;
+ int cmd = 1;
+ BaseParser parser;
+
+ bool itemFound = false, itemsFound = false;
+
+ while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_GAME:
+ if (DID_FAIL(BaseGame::loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_AD_GAME:
+ while (cmd > 0 && (cmd = parser.getCommand((char **)&params, commands, (char **)&params2)) > 0) {
+ switch (cmd) {
+ case TOKEN_RESPONSE_BOX:
+ delete _responseBox;
+ _responseBox = new AdResponseBox(_gameRef);
+ if (_responseBox && !DID_FAIL(_responseBox->loadFile((char *)params2))) {
+ registerObject(_responseBox);
+ } else {
+ delete _responseBox;
+ _responseBox = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_INVENTORY_BOX:
+ delete _inventoryBox;
+ _inventoryBox = new AdInventoryBox(_gameRef);
+ if (_inventoryBox && !DID_FAIL(_inventoryBox->loadFile((char *)params2))) {
+ registerObject(_inventoryBox);
+ } else {
+ delete _inventoryBox;
+ _inventoryBox = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_ITEMS:
+ itemsFound = true;
+ BaseUtils::setString(&_itemsFile, (char *)params2);
+ if (DID_FAIL(loadItemsFile(_itemsFile))) {
+ delete[] _itemsFile;
+ _itemsFile = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_TALK_SKIP_BUTTON:
+ if (scumm_stricmp((char *)params2, "right") == 0) {
+ _talkSkipButton = TALK_SKIP_RIGHT;
+ } else if (scumm_stricmp((char *)params2, "both") == 0) {
+ _talkSkipButton = TALK_SKIP_BOTH;
+ } else {
+ _talkSkipButton = TALK_SKIP_LEFT;
+ }
+ break;
+
+ case TOKEN_SCENE_VIEWPORT: {
+ Rect32 rc;
+ parser.scanStr((char *)params2, "%d,%d,%d,%d", &rc.left, &rc.top, &rc.right, &rc.bottom);
+ if (!_sceneViewport) {
+ _sceneViewport = new BaseViewport(_gameRef);
+ }
+ if (_sceneViewport) {
+ _sceneViewport->setRect(rc.left, rc.top, rc.right, rc.bottom);
+ }
+ }
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params2, false);
+ break;
+
+ case TOKEN_STARTUP_SCENE:
+ BaseUtils::setString(&_startupScene, (char *)params2);
+ break;
+
+ case TOKEN_DEBUG_STARTUP_SCENE:
+ BaseUtils::setString(&_debugStartupScene, (char *)params2);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in GAME definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading GAME definition");
+ return STATUS_FAILED;
+ }
+
+ if (itemFound && !itemsFound) {
+ _gameRef->LOG(0, "**Warning** Please put the items definition to a separate file.");
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::persist(BasePersistenceManager *persistMgr) {
+ if (!persistMgr->getIsSaving()) {
+ cleanup();
+ }
+ BaseGame::persist(persistMgr);
+
+ _dlgPendingBranches.persist(persistMgr);
+
+ _inventories.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_inventoryBox));
+
+ _objects.persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_prevSceneName));
+ persistMgr->transfer(TMEMBER(_prevSceneFilename));
+
+ persistMgr->transfer(TMEMBER(_responseBox));
+ _responsesBranch.persist(persistMgr);
+ _responsesGame.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_scene));
+ _sceneStates.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_scheduledFadeIn));
+ persistMgr->transfer(TMEMBER(_scheduledScene));
+ persistMgr->transfer(TMEMBER(_selectedItem));
+ persistMgr->transfer(TMEMBER_INT(_talkSkipButton));
+
+ _sentences.persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_sceneViewport));
+ persistMgr->transfer(TMEMBER_INT(_stateEx));
+ persistMgr->transfer(TMEMBER(_initialScene));
+ persistMgr->transfer(TMEMBER(_debugStartupScene));
+
+ persistMgr->transfer(TMEMBER(_invObject));
+ persistMgr->transfer(TMEMBER(_inventoryOwner));
+ persistMgr->transfer(TMEMBER(_tempDisableSaveState));
+ _items.persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_itemsFile));
+
+ _speechDirs.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_smartItemCursor));
+
+ if (!persistMgr->getIsSaving()) {
+ _initialScene = false;
+ }
+
+ persistMgr->transfer(TMEMBER(_startupScene));
+
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void AdGame::setPrevSceneName(const char *name) {
+ delete[] _prevSceneName;
+ _prevSceneName = NULL;
+ if (name) {
+ _prevSceneName = new char[strlen(name) + 1];
+ if (_prevSceneName) {
+ strcpy(_prevSceneName, name);
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdGame::setPrevSceneFilename(const char *name) {
+ delete[] _prevSceneFilename;
+ _prevSceneFilename = NULL;
+ if (name) {
+ _prevSceneFilename = new char[strlen(name) + 1];
+ if (_prevSceneFilename) {
+ strcpy(_prevSceneFilename, name);
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::scheduleChangeScene(const char *filename, bool fadeIn) {
+ delete[] _scheduledScene;
+ _scheduledScene = NULL;
+
+ if (_scene && !_scene->_initialized) {
+ return changeScene(filename, fadeIn);
+ } else {
+ _scheduledScene = new char [strlen(filename) + 1];
+ strcpy(_scheduledScene, filename);
+
+ _scheduledFadeIn = fadeIn;
+
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor) {
+ BaseGame::getVersion(verMajor, verMinor, NULL, NULL);
+
+ if (extMajor) {
+ *extMajor = 0;
+ }
+ if (extMinor) {
+ *extMinor = 0;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::loadItemsFile(const char *filename, bool merge) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdGame::LoadItemsFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ //_filename = new char [strlen(filename)+1];
+ //strcpy(_filename, filename);
+
+ if (DID_FAIL(ret = loadItemsBuffer(buffer, merge))) {
+ _gameRef->LOG(0, "Error parsing ITEMS file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::loadItemsBuffer(byte *buffer, bool merge) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ITEM)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (!merge) {
+ while (_items.size() > 0) {
+ deleteItem(_items[0]);
+ }
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_ITEM: {
+ AdItem *item = new AdItem(_gameRef);
+ if (item && !DID_FAIL(item->loadBuffer(params, false))) {
+ // delete item with the same name, if exists
+ if (merge) {
+ AdItem *prevItem = getItemByName(item->getName());
+ if (prevItem) {
+ deleteItem(prevItem);
+ }
+ }
+ addItem(item);
+ } else {
+ delete item;
+ item = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ }
+ break;
+ }
+ }
+
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in ITEMS definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading ITEMS definition");
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdSceneState *AdGame::getSceneState(const char *filename, bool saving) {
+ char *filenameCor = new char[strlen(filename) + 1];
+ strcpy(filenameCor, filename);
+ for (uint32 i = 0; i < strlen(filenameCor); i++) {
+ if (filenameCor[i] == '/') {
+ filenameCor[i] = '\\';
+ }
+ }
+
+ for (uint32 i = 0; i < _sceneStates.size(); i++) {
+ if (scumm_stricmp(_sceneStates[i]->_filename, filenameCor) == 0) {
+ delete[] filenameCor;
+ return _sceneStates[i];
+ }
+ }
+
+ if (saving) {
+ AdSceneState *ret = new AdSceneState(_gameRef);
+ ret->setFilename(filenameCor);
+
+ _sceneStates.add(ret);
+
+ delete[] filenameCor;
+ return ret;
+ } else {
+ delete[] filenameCor;
+ return NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::windowLoadHook(UIWindow *win, char **buffer, char **params) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ENTITY_CONTAINER)
+ TOKEN_TABLE_END
+
+ int cmd = PARSERR_GENERIC;
+ BaseParser parser;
+
+ cmd = parser.getCommand(buffer, commands, params);
+ switch (cmd) {
+ case TOKEN_ENTITY_CONTAINER: {
+ UIEntity *ent = new UIEntity(_gameRef);
+ if (!ent || DID_FAIL(ent->loadBuffer((byte *)*params, false))) {
+ delete ent;
+ ent = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ ent->_parent = win;
+ win->_widgets.add(ent);
+ }
+ }
+ break;
+ }
+
+ if (cmd == PARSERR_TOKENNOTFOUND || cmd == PARSERR_GENERIC) {
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name) {
+ if (strcmp(name, "CreateEntityContainer") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ UIEntity *ent = new UIEntity(_gameRef);
+ if (!val->isNULL()) {
+ ent->setName(val->getString());
+ }
+ stack->pushNative(ent, true);
+
+ ent->_parent = win;
+ win->_widgets.add(ent);
+
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::startDlgBranch(const char *branchName, const char *scriptName, const char *eventName) {
+ char *name = new char[strlen(branchName) + 1 + strlen(scriptName) + 1 + strlen(eventName) + 1];
+ if (name) {
+ sprintf(name, "%s.%s.%s", branchName, scriptName, eventName);
+ _dlgPendingBranches.add(name);
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::endDlgBranch(const char *branchName, const char *scriptName, const char *eventName) {
+ char *name = NULL;
+ bool deleteName = false;
+ if (branchName == NULL && _dlgPendingBranches.size() > 0) {
+ name = _dlgPendingBranches[_dlgPendingBranches.size() - 1];
+ } else {
+ if (branchName != NULL) {
+ name = new char[strlen(branchName) + 1 + strlen(scriptName) + 1 + strlen(eventName) + 1];
+ if (name) {
+ sprintf(name, "%s.%s.%s", branchName, scriptName, eventName);
+ deleteName = true;
+ }
+ }
+ }
+
+ if (name == NULL) {
+ return STATUS_OK;
+ }
+
+
+ int startIndex = -1;
+ for (int i = _dlgPendingBranches.size() - 1; i >= 0; i--) {
+ if (scumm_stricmp(name, _dlgPendingBranches[i]) == 0) {
+ startIndex = i;
+ break;
+ }
+ }
+ if (startIndex >= 0) {
+ for (uint32 i = startIndex; i < _dlgPendingBranches.size(); i++) {
+ //ClearBranchResponses(_dlgPendingBranches[i]);
+ delete[] _dlgPendingBranches[i];
+ _dlgPendingBranches[i] = NULL;
+ }
+ _dlgPendingBranches.remove_at(startIndex, _dlgPendingBranches.size() - startIndex);
+ }
+
+ // dialogue is over, forget selected responses
+ if (_dlgPendingBranches.size() == 0) {
+ for (uint32 i = 0; i < _responsesBranch.size(); i++) {
+ delete _responsesBranch[i];
+ }
+ _responsesBranch.clear();
+ }
+
+ if (deleteName) {
+ delete[] name;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::clearBranchResponses(char *name) {
+ for (uint32 i = 0; i < _responsesBranch.size(); i++) {
+ if (scumm_stricmp(name, _responsesBranch[i]->_context) == 0) {
+ delete _responsesBranch[i];
+ _responsesBranch.remove_at(i);
+ i--;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::addBranchResponse(int id) {
+ if (branchResponseUsed(id)) {
+ return STATUS_OK;
+ }
+ AdResponseContext *r = new AdResponseContext(_gameRef);
+ r->_id = id;
+ r->setContext(_dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL);
+ _responsesBranch.add(r);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::branchResponseUsed(int id) {
+ char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL;
+ for (uint32 i = 0; i < _responsesBranch.size(); i++) {
+ if (_responsesBranch[i]->_id == id) {
+ if ((context == NULL && _responsesBranch[i]->_context == NULL) || scumm_stricmp(context, _responsesBranch[i]->_context) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::addGameResponse(int id) {
+ if (gameResponseUsed(id)) {
+ return STATUS_OK;
+ }
+ AdResponseContext *r = new AdResponseContext(_gameRef);
+ r->_id = id;
+ r->setContext(_dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL);
+ _responsesGame.add(r);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::gameResponseUsed(int id) {
+ char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL;
+ for (uint32 i = 0; i < _responsesGame.size(); i++) {
+ AdResponseContext *respContext = _responsesGame[i];
+ if (respContext->_id == id) {
+ if ((context == NULL && respContext->_context == NULL) || ((context != NULL && respContext->_context != NULL) && scumm_stricmp(context, respContext->_context) == 0)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::resetResponse(int id) {
+ char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL;
+
+ for (uint32 i = 0; i < _responsesGame.size(); i++) {
+ if (_responsesGame[i]->_id == id) {
+ if ((context == NULL && _responsesGame[i]->_context == NULL) || scumm_stricmp(context, _responsesGame[i]->_context) == 0) {
+ delete _responsesGame[i];
+ _responsesGame.remove_at(i);
+ break;
+ }
+ }
+ }
+
+ for (uint32 i = 0; i < _responsesBranch.size(); i++) {
+ if (_responsesBranch[i]->_id == id) {
+ if ((context == NULL && _responsesBranch[i]->_context == NULL) || scumm_stricmp(context, _responsesBranch[i]->_context) == 0) {
+ delete _responsesBranch[i];
+ _responsesBranch.remove_at(i);
+ break;
+ }
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::displayContent(bool doUpdate, bool displayAll) {
+ // init
+ if (doUpdate) {
+ initLoop();
+ }
+
+ // fill black
+ _renderer->fill(0, 0, 0);
+ if (!_editorMode) {
+ _renderer->setScreenViewport();
+ }
+
+ // playing exclusive video?
+ if (_videoPlayer->isPlaying()) {
+ if (doUpdate) {
+ _videoPlayer->update();
+ }
+ _videoPlayer->display();
+ } else if (_theoraPlayer) {
+ if (_theoraPlayer->isPlaying()) {
+ if (doUpdate) {
+ _theoraPlayer->update();
+ }
+ _theoraPlayer->display();
+ }
+ if (_theoraPlayer->isFinished()) {
+ delete _theoraPlayer;
+ _theoraPlayer = NULL;
+ }
+ } else {
+
+ // process scripts
+ if (doUpdate) {
+ _scEngine->tick();
+ }
+
+ Point32 p;
+ getMousePos(&p);
+
+ _scene->update();
+ _scene->display();
+
+
+ // display in-game windows
+ displayWindows(true);
+ if (_inventoryBox) {
+ _inventoryBox->display();
+ }
+ if (_stateEx == GAME_WAITING_RESPONSE) {
+ _responseBox->display();
+ }
+ _renderer->displayIndicator();
+
+
+ if (doUpdate || displayAll) {
+ // display normal windows
+ displayWindows(false);
+
+ setActiveObject(_gameRef->_renderer->getObjectAt(p.x, p.y));
+
+ // textual info
+ displaySentences(_state == GAME_FROZEN);
+
+ showCursor();
+
+ if (_fader) {
+ _fader->display();
+ }
+ _transMgr->update();
+ }
+
+ }
+ if (_loadingIcon) {
+ _loadingIcon->display(_loadingIconX, _loadingIconY);
+ if (!_loadingIconPersistent) {
+ delete _loadingIcon;
+ _loadingIcon = NULL;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::registerInventory(AdInventory *inv) {
+ for (uint32 i = 0; i < _inventories.size(); i++) {
+ if (_inventories[i] == inv) {
+ return STATUS_OK;
+ }
+ }
+ registerObject(inv);
+ _inventories.add(inv);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::unregisterInventory(AdInventory *inv) {
+ for (uint32 i = 0; i < _inventories.size(); i++) {
+ if (_inventories[i] == inv) {
+ unregisterObject(_inventories[i]);
+ _inventories.remove_at(i);
+ return STATUS_OK;
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::isItemTaken(char *itemName) {
+ for (uint32 i = 0; i < _inventories.size(); i++) {
+ AdInventory *inv = _inventories[i];
+
+ for (uint32 j = 0; j < inv->_takenItems.size(); j++) {
+ if (scumm_stricmp(itemName, inv->_takenItems[j]->getName()) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AdItem *AdGame::getItemByName(const char *name) {
+ for (uint32 i = 0; i < _items.size(); i++) {
+ if (scumm_stricmp(_items[i]->getName(), name) == 0) {
+ return _items[i];
+ }
+ }
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::addItem(AdItem *item) {
+ _items.add(item);
+ return _gameRef->registerObject(item);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::resetContent() {
+ // clear pending dialogs
+ for (uint32 i = 0; i < _dlgPendingBranches.size(); i++) {
+ delete[] _dlgPendingBranches[i];
+ }
+ _dlgPendingBranches.clear();
+
+
+ // clear inventories
+ for (uint32 i = 0; i < _inventories.size(); i++) {
+ _inventories[i]->_takenItems.clear();
+ }
+
+ // clear scene states
+ for (uint32 i = 0; i < _sceneStates.size(); i++) {
+ delete _sceneStates[i];
+ }
+ _sceneStates.clear();
+
+ // clear once responses
+ for (uint32 i = 0; i < _responsesBranch.size(); i++) {
+ delete _responsesBranch[i];
+ }
+ _responsesBranch.clear();
+
+ // clear once game responses
+ for (uint32 i = 0; i < _responsesGame.size(); i++) {
+ delete _responsesGame[i];
+ }
+ _responsesGame.clear();
+
+ // reload inventory items
+ if (_itemsFile) {
+ loadItemsFile(_itemsFile);
+ }
+
+ _tempDisableSaveState = true;
+
+ return BaseGame::resetContent();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::deleteItem(AdItem *item) {
+ if (!item) {
+ return STATUS_FAILED;
+ }
+
+ if (_selectedItem == item) {
+ _selectedItem = NULL;
+ }
+ _scene->handleItemAssociations(item->getName(), false);
+
+ // remove from all inventories
+ for (uint32 i = 0; i < _inventories.size(); i++) {
+ _inventories[i]->removeItem(item);
+ }
+
+ // remove object
+ for (uint32 i = 0; i < _items.size(); i++) {
+ if (_items[i] == item) {
+ unregisterObject(_items[i]);
+ _items.remove_at(i);
+ break;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::addSpeechDir(const char *dir) {
+ if (!dir || dir[0] == '\0') {
+ return STATUS_FAILED;
+ }
+
+ char *temp = new char[strlen(dir) + 2];
+ strcpy(temp, dir);
+ if (temp[strlen(temp) - 1] != '\\' && temp[strlen(temp) - 1] != '/') {
+ strcat(temp, "\\");
+ }
+
+ for (uint32 i = 0; i < _speechDirs.size(); i++) {
+ if (scumm_stricmp(_speechDirs[i], temp) == 0) {
+ delete[] temp;
+ return STATUS_OK;
+ }
+ }
+ _speechDirs.add(temp);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::removeSpeechDir(const char *dir) {
+ if (!dir || dir[0] == '\0') {
+ return STATUS_FAILED;
+ }
+
+ char *temp = new char[strlen(dir) + 2];
+ strcpy(temp, dir);
+ if (temp[strlen(temp) - 1] != '\\' && temp[strlen(temp) - 1] != '/') {
+ strcat(temp, "\\");
+ }
+
+ bool found = false;
+ for (uint32 i = 0; i < _speechDirs.size(); i++) {
+ if (scumm_stricmp(_speechDirs[i], temp) == 0) {
+ delete[] _speechDirs[i];
+ _speechDirs.remove_at(i);
+ found = true;
+ break;
+ }
+ }
+ delete[] temp;
+
+ return found;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *AdGame::findSpeechFile(char *stringID) {
+ char *ret = new char[MAX_PATH_LENGTH];
+
+ for (uint32 i = 0; i < _speechDirs.size(); i++) {
+ sprintf(ret, "%s%s.ogg", _speechDirs[i], stringID);
+ if (BaseFileManager::getEngineInstance()->hasFile(ret)) {
+ return ret;
+ }
+
+ sprintf(ret, "%s%s.wav", _speechDirs[i], stringID);
+ if (BaseFileManager::getEngineInstance()->hasFile(ret)) {
+ return ret;
+ }
+ }
+ delete[] ret;
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::validMouse() {
+ Point32 pos;
+ BasePlatform::getCursorPos(&pos);
+
+ return _renderer->pointInViewport(&pos);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::onMouseLeftDown() {
+ if (!validMouse()) {
+ return STATUS_OK;
+ }
+ if (_state == GAME_RUNNING && !_interactive) {
+ if (_talkSkipButton == TALK_SKIP_LEFT || _talkSkipButton == TALK_SKIP_BOTH) {
+ finishSentences();
+ }
+ return STATUS_OK;
+ }
+
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_LEFT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftClick"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("LeftClick");
+ } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
+ _scene->applyEvent("LeftClick");
+ }
+ }
+
+ if (_activeObject != NULL) {
+ _gameRef->_capturedObject = _gameRef->_activeObject;
+ }
+ _mouseLeftDown = true;
+ BasePlatform::setCapture(/*_renderer->_window*/);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::onMouseLeftUp() {
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_LEFT);
+ }
+
+ BasePlatform::releaseCapture();
+ _capturedObject = NULL;
+ _mouseLeftDown = false;
+
+ bool handled = /*_state==GAME_RUNNING &&*/ DID_SUCCEED(applyEvent("LeftRelease"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("LeftRelease");
+ } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
+ _scene->applyEvent("LeftRelease");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::onMouseLeftDblClick() {
+ if (!validMouse()) {
+ return STATUS_OK;
+ }
+
+ if (_state == GAME_RUNNING && !_interactive) {
+ return STATUS_OK;
+ }
+
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_LEFT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftDoubleClick"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("LeftDoubleClick");
+ } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
+ _scene->applyEvent("LeftDoubleClick");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::onMouseRightDown() {
+ if (!validMouse()) {
+ return STATUS_OK;
+ }
+ if (_state == GAME_RUNNING && !_interactive) {
+ if (_talkSkipButton == TALK_SKIP_RIGHT || _talkSkipButton == TALK_SKIP_BOTH) {
+ finishSentences();
+ }
+ return STATUS_OK;
+ }
+
+ if ((_state == GAME_RUNNING && !_interactive) || _stateEx == GAME_WAITING_RESPONSE) {
+ return STATUS_OK;
+ }
+
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_RIGHT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightClick"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("RightClick");
+ } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
+ _scene->applyEvent("RightClick");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::onMouseRightUp() {
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_RIGHT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightRelease"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("RightRelease");
+ } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
+ _scene->applyEvent("RightRelease");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::displayDebugInfo() {
+ char str[100];
+ if (_gameRef->_debugDebugMode) {
+ sprintf(str, "Mouse: %d, %d (scene: %d, %d)", _mousePos.x, _mousePos.y, _mousePos.x + _scene->getOffsetLeft(), _mousePos.y + _scene->getOffsetTop());
+ _systemFont->drawText((byte *)str, 0, 90, _renderer->_width, TAL_RIGHT);
+
+ sprintf(str, "Scene: %s (prev: %s)", (_scene && _scene->getName()) ? _scene->getName() : "???", _prevSceneName ? _prevSceneName : "???");
+ _systemFont->drawText((byte *)str, 0, 110, _renderer->_width, TAL_RIGHT);
+ }
+ return BaseGame::displayDebugInfo();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdGame::onScriptShutdown(ScScript *script) {
+ if (_responseBox && _responseBox->_waitingScript == script) {
+ _responseBox->_waitingScript = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_game.h b/engines/wintermute/ad/ad_game.h
new file mode 100644
index 0000000000..81c79a3da8
--- /dev/null
+++ b/engines/wintermute/ad/ad_game.h
@@ -0,0 +1,163 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+#ifndef WINTERMUTE_ADGAME_H
+#define WINTERMUTE_ADGAME_H
+
+#include "engines/wintermute/ad/ad_types.h"
+#include "engines/wintermute/base/base_game.h"
+
+namespace Wintermute {
+class AdItem;
+class AdInventory;
+class AdSceneState;
+class AdScene;
+class AdItem;
+class AdObject;
+class AdSentence;
+class AdInventoryBox;
+class AdResponseContext;
+class AdResponseBox;
+class AdGame : public BaseGame {
+public:
+ virtual bool onScriptShutdown(ScScript *script);
+
+ virtual bool onMouseLeftDown();
+ virtual bool onMouseLeftUp();
+ virtual bool onMouseLeftDblClick();
+ virtual bool onMouseRightDown();
+ virtual bool onMouseRightUp();
+
+ virtual bool displayDebugInfo();
+
+ bool addSpeechDir(const char *dir);
+ bool removeSpeechDir(const char *dir);
+ char *findSpeechFile(char *StringID);
+
+ bool deleteItem(AdItem *Item);
+ char *_itemsFile;
+ bool _tempDisableSaveState;
+ virtual bool resetContent();
+ bool addItem(AdItem *item);
+ AdItem *getItemByName(const char *name);
+
+ AdObject *_inventoryOwner;
+ bool isItemTaken(char *itemName);
+ bool registerInventory(AdInventory *inv);
+ bool unregisterInventory(AdInventory *inv);
+ virtual bool displayContent(bool update = true, bool displayAll = false);
+
+ bool gameResponseUsed(int ID);
+ bool addGameResponse(int ID);
+ bool resetResponse(int ID);
+
+ bool branchResponseUsed(int ID);
+ bool addBranchResponse(int ID);
+ bool clearBranchResponses(char *name);
+ bool startDlgBranch(const char *branchName, const char *scriptName, const char *eventName);
+ bool endDlgBranch(const char *branchName, const char *scriptName, const char *eventName);
+ virtual bool windowLoadHook(UIWindow *win, char **buf, char **params);
+ virtual bool windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name);
+
+ AdSceneState *getSceneState(const char *filename, bool saving);
+ BaseViewport *_sceneViewport;
+
+ int _texItemLifeTime;
+ int _texWalkLifeTime;
+ int _texStandLifeTime;
+ int _texTalkLifeTime;
+
+ TTalkSkipButton _talkSkipButton;
+
+ virtual bool getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor);
+ bool scheduleChangeScene(const char *filename, bool fadeIn);
+ void setPrevSceneName(const char *name);
+ void setPrevSceneFilename(const char *name);
+
+ AdItem *_selectedItem;
+ bool cleanup();
+ DECLARE_PERSISTENT(AdGame, BaseGame)
+
+ void finishSentences();
+ bool showCursor();
+
+ TGameStateEx _stateEx;
+
+ bool displaySentences(bool frozen);
+ void addSentence(AdSentence *sentence);
+ bool changeScene(const char *filename, bool fadeIn);
+ bool removeObject(AdObject *object);
+ bool addObject(AdObject *object);
+ AdScene *_scene;
+ bool initLoop();
+ AdGame(const Common::String &gameId);
+ virtual ~AdGame();
+
+ BaseArray<AdObject *> _objects;
+
+ virtual bool loadFile(const char *filename);
+ virtual bool loadBuffer(byte *buffer, bool complete = true);
+
+ bool loadItemsFile(const char *filename, bool merge = false);
+ bool loadItemsBuffer(byte *buffer, bool merge = false);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ bool validMouse();
+private:
+ virtual bool externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name);
+
+ AdObject *_invObject;
+ BaseArray<AdInventory *> _inventories;
+ char *_scheduledScene;
+ bool _scheduledFadeIn;
+ char *_prevSceneName;
+ char *_prevSceneFilename;
+ char *_debugStartupScene;
+ char *_startupScene;
+ bool _initialScene;
+ bool _smartItemCursor;
+ BaseArray<char *> _speechDirs;
+ BaseArray<AdItem *> _items;
+
+ BaseArray<AdSentence *> _sentences;
+
+ BaseArray<AdSceneState *> _sceneStates;
+ BaseArray<char *> _dlgPendingBranches;
+
+ BaseArray<AdResponseContext *> _responsesBranch;
+ BaseArray<AdResponseContext *> _responsesGame;
+
+ AdResponseBox *_responseBox;
+ AdInventoryBox *_inventoryBox;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_inventory.cpp b/engines/wintermute/ad/ad_inventory.cpp
new file mode 100644
index 0000000000..72f8fa0fb4
--- /dev/null
+++ b/engines/wintermute/ad/ad_inventory.cpp
@@ -0,0 +1,136 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_inventory.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_item.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdInventory, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdInventory::AdInventory(BaseGame *inGame) : BaseObject(inGame) {
+ _scrollOffset = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdInventory::~AdInventory() {
+ _takenItems.clear(); // ref only
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventory::insertItem(const char *name, const char *insertAfter) {
+ if (name == NULL) {
+ return STATUS_FAILED;
+ }
+
+ AdItem *item = ((AdGame *)_gameRef)->getItemByName(name);
+ if (item == NULL) {
+ return STATUS_FAILED;
+ }
+
+ int insertIndex = -1;
+ for (uint32 i = 0; i < _takenItems.size(); i++) {
+ if (scumm_stricmp(_takenItems[i]->getName(), name) == 0) {
+ _takenItems.remove_at(i);
+ i--;
+ continue;
+ }
+ if (insertAfter && scumm_stricmp(_takenItems[i]->getName(), insertAfter) == 0) {
+ insertIndex = i + 1;
+ }
+ }
+
+
+ if (insertIndex == -1) {
+ _takenItems.add(item);
+ } else {
+ _takenItems.insert_at(insertIndex, item);
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventory::removeItem(const char *name) {
+ if (name == NULL) {
+ return STATUS_FAILED;
+ }
+
+ for (uint32 i = 0; i < _takenItems.size(); i++) {
+ if (scumm_stricmp(_takenItems[i]->getName(), name) == 0) {
+ if (((AdGame *)_gameRef)->_selectedItem == _takenItems[i]) {
+ ((AdGame *)_gameRef)->_selectedItem = NULL;
+ }
+ _takenItems.remove_at(i);
+ return STATUS_OK;
+ }
+ }
+
+ return STATUS_FAILED;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventory::removeItem(AdItem *item) {
+ if (item == NULL) {
+ return STATUS_FAILED;
+ }
+
+ for (uint32 i = 0; i < _takenItems.size(); i++) {
+ if (_takenItems[i] == item) {
+ if (((AdGame *)_gameRef)->_selectedItem == _takenItems[i]) {
+ ((AdGame *)_gameRef)->_selectedItem = NULL;
+ }
+ _takenItems.remove_at(i);
+ return STATUS_OK;
+ }
+ }
+
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventory::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ _takenItems.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_scrollOffset));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_inventory.h b/engines/wintermute/ad/ad_inventory.h
new file mode 100644
index 0000000000..4017d914bc
--- /dev/null
+++ b/engines/wintermute/ad/ad_inventory.h
@@ -0,0 +1,52 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADINVENTORY_H
+#define WINTERMUTE_ADINVENTORY_H
+
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+
+class AdItem;
+
+class AdInventory : public BaseObject {
+public:
+ DECLARE_PERSISTENT(AdInventory, BaseObject)
+ bool removeItem(const char *name);
+ bool removeItem(AdItem *Item);
+ bool insertItem(const char *name, const char *insertAfter = NULL);
+ AdInventory(BaseGame *inGame);
+ virtual ~AdInventory();
+ BaseArray<AdItem *> _takenItems;
+ int _scrollOffset;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_inventory_box.cpp b/engines/wintermute/ad/ad_inventory_box.cpp
new file mode 100644
index 0000000000..7ae8ff8d69
--- /dev/null
+++ b/engines/wintermute/ad/ad_inventory_box.cpp
@@ -0,0 +1,389 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_inventory_box.h"
+#include "engines/wintermute/ad/ad_inventory.h"
+#include "engines/wintermute/ad/ad_item.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_viewport.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/ui/ui_button.h"
+#include "engines/wintermute/ui/ui_window.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+#include "common/rect.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdInventoryBox, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdInventoryBox::AdInventoryBox(BaseGame *inGame) : BaseObject(inGame) {
+ _itemsArea.setEmpty();
+ _scrollOffset = 0;
+ _spacing = 0;
+ _itemWidth = _itemHeight = 50;
+ _scrollBy = 1;
+
+ _window = NULL;
+ _closeButton = NULL;
+
+ _hideSelected = false;
+
+ _visible = false;
+ _exclusive = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdInventoryBox::~AdInventoryBox() {
+ _gameRef->unregisterObject(_window);
+ _window = NULL;
+
+ delete _closeButton;
+ _closeButton = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventoryBox::listen(BaseScriptHolder *param1, uint32 param2) {
+ UIObject *obj = (UIObject *)param1;
+
+ switch (obj->_type) {
+ case UI_BUTTON:
+ if (scumm_stricmp(obj->getName(), "close") == 0) {
+ _visible = false;
+ } else if (scumm_stricmp(obj->getName(), "prev") == 0) {
+ _scrollOffset -= _scrollBy;
+ _scrollOffset = MAX(_scrollOffset, 0);
+ } else if (scumm_stricmp(obj->getName(), "next") == 0) {
+ _scrollOffset += _scrollBy;
+ } else {
+ return BaseObject::listen(param1, param2);
+ }
+ break;
+ default:
+ error("AdInventoryBox::Listen - Unhandled enum");
+ break;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventoryBox::display() {
+ AdGame *adGame = (AdGame *)_gameRef;
+
+ if (!_visible) {
+ return STATUS_OK;
+ }
+
+ int itemsX, itemsY;
+ itemsX = (int)floor((float)((_itemsArea.right - _itemsArea.left + _spacing) / (_itemWidth + _spacing)));
+ itemsY = (int)floor((float)((_itemsArea.bottom - _itemsArea.top + _spacing) / (_itemHeight + _spacing)));
+
+ if (_window) {
+ _window->enableWidget("prev", _scrollOffset > 0);
+ _window->enableWidget("next", _scrollOffset + itemsX * itemsY < (int32)adGame->_inventoryOwner->getInventory()->_takenItems.size());
+ }
+
+
+ if (_closeButton) {
+ _closeButton->_posX = _closeButton->_posY = 0;
+ _closeButton->_width = _gameRef->_renderer->_width;
+ _closeButton->_height = _gameRef->_renderer->_height;
+
+ _closeButton->display();
+ }
+
+
+ // display window
+ Rect32 rect = _itemsArea;
+ if (_window) {
+ rect.offsetRect(_window->_posX, _window->_posY);
+ _window->display();
+ }
+
+ // display items
+ if (_window && _window->_alphaColor != 0) {
+ _gameRef->_renderer->_forceAlphaColor = _window->_alphaColor;
+ }
+ int yyy = rect.top;
+ for (int j = 0; j < itemsY; j++) {
+ int xxx = rect.left;
+ for (int i = 0; i < itemsX; i++) {
+ int itemIndex = _scrollOffset + j * itemsX + i;
+ if (itemIndex >= 0 && itemIndex < (int32)adGame->_inventoryOwner->getInventory()->_takenItems.size()) {
+ AdItem *item = adGame->_inventoryOwner->getInventory()->_takenItems[itemIndex];
+ if (item != ((AdGame *)_gameRef)->_selectedItem || !_hideSelected) {
+ item->update();
+ item->display(xxx, yyy);
+ }
+ }
+
+ xxx += (_itemWidth + _spacing);
+ }
+ yyy += (_itemHeight + _spacing);
+ }
+ if (_window && _window->_alphaColor != 0) {
+ _gameRef->_renderer->_forceAlphaColor = 0;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventoryBox::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdInventoryBox::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing INVENTORY_BOX file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(INVENTORY_BOX)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(WINDOW)
+TOKEN_DEF(EXCLUSIVE)
+TOKEN_DEF(ALWAYS_VISIBLE)
+TOKEN_DEF(AREA)
+TOKEN_DEF(SPACING)
+TOKEN_DEF(ITEM_WIDTH)
+TOKEN_DEF(ITEM_HEIGHT)
+TOKEN_DEF(SCROLL_BY)
+TOKEN_DEF(NAME)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(HIDE_SELECTED)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdInventoryBox::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(INVENTORY_BOX)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(WINDOW)
+ TOKEN_TABLE(EXCLUSIVE)
+ TOKEN_TABLE(ALWAYS_VISIBLE)
+ TOKEN_TABLE(AREA)
+ TOKEN_TABLE(SPACING)
+ TOKEN_TABLE(ITEM_WIDTH)
+ TOKEN_TABLE(ITEM_HEIGHT)
+ TOKEN_TABLE(SCROLL_BY)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(HIDE_SELECTED)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd = 2;
+ BaseParser parser;
+ bool alwaysVisible = false;
+
+ _exclusive = false;
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_INVENTORY_BOX) {
+ _gameRef->LOG(0, "'INVENTORY_BOX' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_WINDOW:
+ delete _window;
+ _window = new UIWindow(_gameRef);
+ if (!_window || DID_FAIL(_window->loadBuffer(params, false))) {
+ delete _window;
+ _window = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ _gameRef->registerObject(_window);
+ }
+ break;
+
+ case TOKEN_AREA:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_itemsArea.left, &_itemsArea.top, &_itemsArea.right, &_itemsArea.bottom);
+ break;
+
+ case TOKEN_EXCLUSIVE:
+ parser.scanStr((char *)params, "%b", &_exclusive);
+ break;
+
+ case TOKEN_HIDE_SELECTED:
+ parser.scanStr((char *)params, "%b", &_hideSelected);
+ break;
+
+ case TOKEN_ALWAYS_VISIBLE:
+ parser.scanStr((char *)params, "%b", &alwaysVisible);
+ break;
+
+ case TOKEN_SPACING:
+ parser.scanStr((char *)params, "%d", &_spacing);
+ break;
+
+ case TOKEN_ITEM_WIDTH:
+ parser.scanStr((char *)params, "%d", &_itemWidth);
+ break;
+
+ case TOKEN_ITEM_HEIGHT:
+ parser.scanStr((char *)params, "%d", &_itemHeight);
+ break;
+
+ case TOKEN_SCROLL_BY:
+ parser.scanStr((char *)params, "%d", &_scrollBy);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in INVENTORY_BOX definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading INVENTORY_BOX definition");
+ return STATUS_FAILED;
+ }
+
+ if (_exclusive) {
+ delete _closeButton;
+ _closeButton = new UIButton(_gameRef);
+ if (_closeButton) {
+ _closeButton->setName("close");
+ _closeButton->setListener(this, _closeButton, 0);
+ _closeButton->_parent = _window;
+ }
+ }
+
+ _visible = alwaysVisible;
+
+ if (_window) {
+ for (uint32 i = 0; i < _window->_widgets.size(); i++) {
+ if (!_window->_widgets[i]->_listenerObject) {
+ _window->_widgets[i]->setListener(this, _window->_widgets[i], 0);
+ }
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventoryBox::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "INVENTORY_BOX\n");
+ buffer->putTextIndent(indent, "{\n");
+
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+
+ buffer->putTextIndent(indent + 2, "AREA { %d, %d, %d, %d }\n", _itemsArea.left, _itemsArea.top, _itemsArea.right, _itemsArea.bottom);
+
+ buffer->putTextIndent(indent + 2, "EXCLUSIVE=%s\n", _exclusive ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "HIDE_SELECTED=%s\n", _hideSelected ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "ALWAYS_VISIBLE=%s\n", _visible ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "SPACING=%d\n", _spacing);
+ buffer->putTextIndent(indent + 2, "ITEM_WIDTH=%d\n", _itemWidth);
+ buffer->putTextIndent(indent + 2, "ITEM_HEIGHT=%d\n", _itemHeight);
+ buffer->putTextIndent(indent + 2, "SCROLL_BY=%d\n", _scrollBy);
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // window
+ if (_window) {
+ _window->saveAsText(buffer, indent + 2);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // editor properties
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdInventoryBox::persist(BasePersistenceManager *persistMgr) {
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_closeButton));
+ persistMgr->transfer(TMEMBER(_hideSelected));
+ persistMgr->transfer(TMEMBER(_itemHeight));
+ persistMgr->transfer(TMEMBER(_itemsArea));
+ persistMgr->transfer(TMEMBER(_itemWidth));
+ persistMgr->transfer(TMEMBER(_scrollBy));
+ persistMgr->transfer(TMEMBER(_scrollOffset));
+ persistMgr->transfer(TMEMBER(_spacing));
+ persistMgr->transfer(TMEMBER(_visible));
+ persistMgr->transfer(TMEMBER(_window));
+ persistMgr->transfer(TMEMBER(_exclusive));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_inventory_box.h b/engines/wintermute/ad/ad_inventory_box.h
new file mode 100644
index 0000000000..cb6d084562
--- /dev/null
+++ b/engines/wintermute/ad/ad_inventory_box.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADINVENTORYBOX_H
+#define WINTERMUTE_ADINVENTORYBOX_H
+
+#include "engines/wintermute/base/base_object.h"
+#include "common/rect.h"
+
+namespace Wintermute {
+class UIButton;
+class UIWindow;
+
+class AdInventoryBox : public BaseObject {
+public:
+ bool _hideSelected;
+ DECLARE_PERSISTENT(AdInventoryBox, BaseObject)
+ bool _visible;
+ virtual bool display();
+ UIButton *_closeButton;
+ int _spacing;
+ int _scrollOffset;
+ Rect32 _itemsArea;
+ bool listen(BaseScriptHolder *param1, uint32 param2);
+ UIWindow *_window;
+ AdInventoryBox(BaseGame *inGame);
+ virtual ~AdInventoryBox();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+private:
+ bool _exclusive;
+ int _scrollBy;
+ int _itemHeight;
+ int _itemWidth;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_item.cpp b/engines/wintermute/ad/ad_item.cpp
new file mode 100644
index 0000000000..427b1c7db4
--- /dev/null
+++ b/engines/wintermute/ad/ad_item.cpp
@@ -0,0 +1,813 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_item.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_sentence.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdItem, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdItem::AdItem(BaseGame *inGame) : AdTalkHolder(inGame) {
+ _spriteHover = NULL;
+ _cursorNormal = _cursorHover = NULL;
+
+ _cursorCombined = true;
+ _inInventory = false;
+
+ _displayAmount = false;
+ _amount = 0;
+ _amountOffsetX = 0;
+ _amountOffsetY = 0;
+ _amountAlign = TAL_RIGHT;
+ _amountString = NULL;
+
+ _state = STATE_READY;
+
+ _movable = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdItem::~AdItem() {
+ delete _spriteHover;
+ delete _cursorNormal;
+ delete _cursorHover;
+ _spriteHover = NULL;
+ _cursorNormal = NULL;
+ _cursorHover = NULL;
+
+ delete[] _amountString;
+ _amountString = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdItem::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdItem::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing ITEM file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(ITEM)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(CURSOR_HOVER)
+TOKEN_DEF(CURSOR_COMBINED)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(NAME)
+TOKEN_DEF(IMAGE_HOVER)
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(EVENTS)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(FONT)
+TOKEN_DEF(ALPHA_COLOR)
+TOKEN_DEF(ALPHA)
+TOKEN_DEF(TALK_SPECIAL)
+TOKEN_DEF(TALK)
+TOKEN_DEF(SPRITE_HOVER)
+TOKEN_DEF(SPRITE)
+TOKEN_DEF(DISPLAY_AMOUNT)
+TOKEN_DEF(AMOUNT_OFFSET_X)
+TOKEN_DEF(AMOUNT_OFFSET_Y)
+TOKEN_DEF(AMOUNT_ALIGN)
+TOKEN_DEF(AMOUNT_STRING)
+TOKEN_DEF(AMOUNT)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdItem::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ITEM)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(CURSOR_HOVER)
+ TOKEN_TABLE(CURSOR_COMBINED)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(IMAGE_HOVER)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(EVENTS)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(ALPHA_COLOR)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE(TALK_SPECIAL)
+ TOKEN_TABLE(TALK)
+ TOKEN_TABLE(SPRITE_HOVER)
+ TOKEN_TABLE(SPRITE)
+ TOKEN_TABLE(DISPLAY_AMOUNT)
+ TOKEN_TABLE(AMOUNT_OFFSET_X)
+ TOKEN_TABLE(AMOUNT_OFFSET_Y)
+ TOKEN_TABLE(AMOUNT_ALIGN)
+ TOKEN_TABLE(AMOUNT_STRING)
+ TOKEN_TABLE(AMOUNT)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd = 2;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_ITEM) {
+ _gameRef->LOG(0, "'ITEM' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ int ar = 0, ag = 0, ab = 0, alpha = 255;
+ while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_FONT:
+ setFont((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_IMAGE:
+ case TOKEN_SPRITE:
+ delete _sprite;
+ _sprite = new BaseSprite(_gameRef, this);
+ if (!_sprite || DID_FAIL(_sprite->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) {
+ delete _sprite;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE_HOVER:
+ case TOKEN_SPRITE_HOVER:
+ delete _spriteHover;
+ _spriteHover = new BaseSprite(_gameRef, this);
+ if (!_spriteHover || DID_FAIL(_spriteHover->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) {
+ delete _spriteHover;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_AMOUNT:
+ parser.scanStr((char *)params, "%d", &_amount);
+ break;
+
+ case TOKEN_DISPLAY_AMOUNT:
+ parser.scanStr((char *)params, "%b", &_displayAmount);
+ break;
+
+ case TOKEN_AMOUNT_OFFSET_X:
+ parser.scanStr((char *)params, "%d", &_amountOffsetX);
+ break;
+
+ case TOKEN_AMOUNT_OFFSET_Y:
+ parser.scanStr((char *)params, "%d", &_amountOffsetY);
+ break;
+
+ case TOKEN_AMOUNT_ALIGN:
+ if (scumm_stricmp((char *)params, "left") == 0) {
+ _amountAlign = TAL_LEFT;
+ } else if (scumm_stricmp((char *)params, "right") == 0) {
+ _amountAlign = TAL_RIGHT;
+ } else {
+ _amountAlign = TAL_CENTER;
+ }
+ break;
+
+ case TOKEN_AMOUNT_STRING:
+ BaseUtils::setString(&_amountString, (char *)params);
+ break;
+
+ case TOKEN_TALK: {
+ BaseSprite *spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, ((AdGame *)_gameRef)->_texTalkLifeTime))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _talkSprites.add(spr);
+ }
+ }
+ break;
+
+ case TOKEN_TALK_SPECIAL: {
+ BaseSprite *spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, ((AdGame *)_gameRef)->_texTalkLifeTime))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _talkSpritesEx.add(spr);
+ }
+ }
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursorNormal;
+ _cursorNormal = new BaseSprite(_gameRef);
+ if (!_cursorNormal || DID_FAIL(_cursorNormal->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) {
+ delete _cursorNormal;
+ _cursorNormal = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_CURSOR_HOVER:
+ delete _cursorHover;
+ _cursorHover = new BaseSprite(_gameRef);
+ if (!_cursorHover || DID_FAIL(_cursorHover->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) {
+ delete _cursorHover;
+ _cursorHover = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_CURSOR_COMBINED:
+ parser.scanStr((char *)params, "%b", &_cursorCombined);
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+
+ case TOKEN_ALPHA_COLOR:
+ parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab);
+ break;
+
+ case TOKEN_ALPHA:
+ parser.scanStr((char *)params, "%d", &alpha);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in ITEM definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading ITEM definition");
+ return STATUS_FAILED;
+ }
+
+ if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) {
+ ar = ag = ab = 255;
+ }
+ _alphaColor = BYTETORGBA(ar, ag, ab, alpha);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdItem::update() {
+ _currentSprite = NULL;
+
+ if (_state == STATE_READY && _animSprite) {
+ delete _animSprite;
+ _animSprite = NULL;
+ }
+
+ // finished playing animation?
+ if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) {
+ _state = STATE_READY;
+ _currentSprite = _animSprite;
+ }
+
+ if (_sentence && _state != STATE_TALKING) {
+ _sentence->finish();
+ }
+
+ // default: stand animation
+ if (!_currentSprite) {
+ _currentSprite = _sprite;
+ }
+
+ switch (_state) {
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_PLAYING_ANIM:
+ _currentSprite = _animSprite;
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_READY:
+ if (!_animSprite) {
+ if (_gameRef->_activeObject == this && _spriteHover) {
+ _currentSprite = _spriteHover;
+ } else {
+ _currentSprite = _sprite;
+ }
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case STATE_TALKING: {
+ _sentence->update();
+ if (_sentence->_currentSprite) {
+ _tempSprite2 = _sentence->_currentSprite;
+ }
+
+ bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime);
+ if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) {
+ if (timeIsUp) {
+ _sentence->finish();
+ _tempSprite2 = NULL;
+ _state = STATE_READY;
+ } else {
+ _tempSprite2 = getTalkStance(_sentence->getNextStance());
+ if (_tempSprite2) {
+ _tempSprite2->reset();
+ _currentSprite = _tempSprite2;
+ }
+ ((AdGame *)_gameRef)->addSentence(_sentence);
+ }
+ } else {
+ _currentSprite = _tempSprite2;
+ ((AdGame *)_gameRef)->addSentence(_sentence);
+ }
+ }
+ default:
+ break;
+ }
+ _ready = (_state == STATE_READY);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdItem::display(int x, int y) {
+ int width = 0;
+ if (_currentSprite) {
+ Rect32 rc;
+ _currentSprite->getBoundingRect(&rc, 0, 0);
+ width = rc.width();
+ }
+
+ _posX = x + width / 2;
+ _posY = y;
+
+ bool ret;
+ if (_currentSprite) {
+ ret = _currentSprite->draw(x, y, this, 100, 100, _alphaColor);
+ } else {
+ ret = STATUS_OK;
+ }
+
+ if (_displayAmount) {
+ int amountX = x;
+ int amountY = y + _amountOffsetY;
+
+ if (_amountAlign == TAL_RIGHT) {
+ width -= _amountOffsetX;
+ amountX -= _amountOffsetX;
+ }
+ amountX += _amountOffsetX;
+
+ BaseFont *font = _font ? _font : _gameRef->_systemFont;
+ if (font) {
+ if (_amountString) {
+ font->drawText((byte *)_amountString, amountX, amountY, width, _amountAlign);
+ } else {
+ char str[256];
+ sprintf(str, "%d", _amount);
+ font->drawText((byte *)str, amountX, amountY, width, _amountAlign);
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdItem::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetHoverSprite
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetHoverSprite") == 0) {
+ stack->correctParams(1);
+
+ bool setCurrent = false;
+ if (_currentSprite && _currentSprite == _spriteHover) {
+ setCurrent = true;
+ }
+
+ const char *filename = stack->pop()->getString();
+
+ delete _spriteHover;
+ _spriteHover = NULL;
+ BaseSprite *spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile(filename))) {
+ stack->pushBool(false);
+ script->runtimeError("Item.SetHoverSprite failed for file '%s'", filename);
+ } else {
+ _spriteHover = spr;
+ if (setCurrent) {
+ _currentSprite = _spriteHover;
+ }
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetHoverSprite
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHoverSprite") == 0) {
+ stack->correctParams(0);
+
+ if (!_spriteHover || !_spriteHover->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_spriteHover->getFilename());
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetHoverSpriteObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHoverSpriteObject") == 0) {
+ stack->correctParams(0);
+ if (!_spriteHover) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_spriteHover, true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetNormalCursor
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetNormalCursor") == 0) {
+ stack->correctParams(1);
+
+ const char *filename = stack->pop()->getString();
+
+ delete _cursorNormal;
+ _cursorNormal = NULL;
+ BaseSprite *spr = new BaseSprite(_gameRef);
+ if (!spr || DID_FAIL(spr->loadFile(filename))) {
+ stack->pushBool(false);
+ script->runtimeError("Item.SetNormalCursor failed for file '%s'", filename);
+ } else {
+ _cursorNormal = spr;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetNormalCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetNormalCursor") == 0) {
+ stack->correctParams(0);
+
+ if (!_cursorNormal || !_cursorNormal->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_cursorNormal->getFilename());
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetNormalCursorObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetNormalCursorObject") == 0) {
+ stack->correctParams(0);
+
+ if (!_cursorNormal) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_cursorNormal, true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetHoverCursor
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetHoverCursor") == 0) {
+ stack->correctParams(1);
+
+ const char *filename = stack->pop()->getString();
+
+ delete _cursorHover;
+ _cursorHover = NULL;
+ BaseSprite *spr = new BaseSprite(_gameRef);
+ if (!spr || DID_FAIL(spr->loadFile(filename))) {
+ stack->pushBool(false);
+ script->runtimeError("Item.SetHoverCursor failed for file '%s'", filename);
+ } else {
+ _cursorHover = spr;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetHoverCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHoverCursor") == 0) {
+ stack->correctParams(0);
+
+ if (!_cursorHover || !_cursorHover->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_cursorHover->getFilename());
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetHoverCursorObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHoverCursorObject") == 0) {
+ stack->correctParams(0);
+
+ if (!_cursorHover) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_cursorHover, true);
+ }
+ return STATUS_OK;
+ } else {
+ return AdTalkHolder::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdItem::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("item");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Name") {
+ _scValue->setString(getName());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DisplayAmount
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "DisplayAmount") {
+ _scValue->setBool(_displayAmount);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Amount
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Amount") {
+ _scValue->setInt(_amount);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AmountOffsetX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AmountOffsetX") {
+ _scValue->setInt(_amountOffsetX);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AmountOffsetY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AmountOffsetY") {
+ _scValue->setInt(_amountOffsetY);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AmountAlign
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AmountAlign") {
+ _scValue->setInt(_amountAlign);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AmountString
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AmountString") {
+ if (!_amountString) {
+ _scValue->setNULL();
+ } else {
+ _scValue->setString(_amountString);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CursorCombined
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "CursorCombined") {
+ _scValue->setBool(_cursorCombined);
+ return _scValue;
+ } else {
+ return AdTalkHolder::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdItem::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DisplayAmount
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DisplayAmount") == 0) {
+ _displayAmount = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Amount
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Amount") == 0) {
+ _amount = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AmountOffsetX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AmountOffsetX") == 0) {
+ _amountOffsetX = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AmountOffsetY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AmountOffsetY") == 0) {
+ _amountOffsetY = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AmountAlign
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AmountAlign") == 0) {
+ _amountAlign = (TTextAlign)value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AmountString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AmountString") == 0) {
+ if (value->isNULL()) {
+ delete[] _amountString;
+ _amountString = NULL;
+ } else {
+ BaseUtils::setString(&_amountString, value->getString());
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CursorCombined
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CursorCombined") == 0) {
+ _cursorCombined = value->getBool();
+ return STATUS_OK;
+ } else {
+ return AdTalkHolder::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdItem::scToString() {
+ return "[item]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdItem::persist(BasePersistenceManager *persistMgr) {
+
+ AdTalkHolder::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_cursorCombined));
+ persistMgr->transfer(TMEMBER(_cursorHover));
+ persistMgr->transfer(TMEMBER(_cursorNormal));
+ persistMgr->transfer(TMEMBER(_spriteHover));
+ persistMgr->transfer(TMEMBER(_inInventory));
+ persistMgr->transfer(TMEMBER(_displayAmount));
+ persistMgr->transfer(TMEMBER(_amount));
+ persistMgr->transfer(TMEMBER(_amountOffsetX));
+ persistMgr->transfer(TMEMBER(_amountOffsetY));
+ persistMgr->transfer(TMEMBER_INT(_amountAlign));
+ persistMgr->transfer(TMEMBER(_amountString));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdItem::getExtendedFlag(const char *flagName) {
+ if (!flagName) {
+ return false;
+ } else if (strcmp(flagName, "usable") == 0) {
+ return true;
+ } else {
+ return AdObject::getExtendedFlag(flagName);
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_item.h b/engines/wintermute/ad/ad_item.h
new file mode 100644
index 0000000000..79978f9f72
--- /dev/null
+++ b/engines/wintermute/ad/ad_item.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADITEM_H
+#define WINTERMUTE_ADITEM_H
+
+
+#include "engines/wintermute/ad/ad_talk_holder.h"
+
+namespace Wintermute {
+
+class AdItem : public AdTalkHolder {
+public:
+ bool update();
+ DECLARE_PERSISTENT(AdItem, AdTalkHolder)
+ bool display(int x, int y);
+ bool getExtendedFlag(const char *flagName);
+ bool _inInventory;
+ bool _cursorCombined;
+ BaseSprite *_spriteHover;
+ BaseSprite *_cursorNormal;
+ BaseSprite *_cursorHover;
+ AdItem(BaseGame *inGame);
+ virtual ~AdItem();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+private:
+ bool _displayAmount;
+ int _amount;
+ int _amountOffsetX;
+ int _amountOffsetY;
+ TTextAlign _amountAlign;
+ char *_amountString;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_layer.cpp b/engines/wintermute/ad/ad_layer.cpp
new file mode 100644
index 0000000000..209c12b7a2
--- /dev/null
+++ b/engines/wintermute/ad/ad_layer.cpp
@@ -0,0 +1,564 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/ad/ad_layer.h"
+#include "engines/wintermute/ad/ad_scene_node.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdLayer, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdLayer::AdLayer(BaseGame *inGame) : BaseObject(inGame) {
+ _main = false;
+ _width = _height = 0;
+ _active = true;
+ _closeUp = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdLayer::~AdLayer() {
+ for (uint32 i = 0; i < _nodes.size(); i++) {
+ delete _nodes[i];
+ }
+ _nodes.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdLayer::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdLayer::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing LAYER file '%s'", filename);
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(LAYER)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(NAME)
+TOKEN_DEF(WIDTH)
+TOKEN_DEF(HEIGHT)
+TOKEN_DEF(MAIN)
+TOKEN_DEF(ENTITY)
+TOKEN_DEF(REGION)
+TOKEN_DEF(ACTIVE)
+TOKEN_DEF(EDITOR_SELECTED)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(CLOSE_UP)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdLayer::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(LAYER)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(WIDTH)
+ TOKEN_TABLE(HEIGHT)
+ TOKEN_TABLE(MAIN)
+ TOKEN_TABLE(ENTITY)
+ TOKEN_TABLE(REGION)
+ TOKEN_TABLE(ACTIVE)
+ TOKEN_TABLE(EDITOR_SELECTED)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(CLOSE_UP)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_LAYER) {
+ _gameRef->LOG(0, "'LAYER' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_MAIN:
+ parser.scanStr((char *)params, "%b", &_main);
+ break;
+
+ case TOKEN_CLOSE_UP:
+ parser.scanStr((char *)params, "%b", &_closeUp);
+ break;
+
+ case TOKEN_WIDTH:
+ parser.scanStr((char *)params, "%d", &_width);
+ break;
+
+ case TOKEN_HEIGHT:
+ parser.scanStr((char *)params, "%d", &_height);
+ break;
+
+ case TOKEN_ACTIVE:
+ parser.scanStr((char *)params, "%b", &_active);
+ break;
+
+ case TOKEN_REGION: {
+ AdRegion *region = new AdRegion(_gameRef);
+ AdSceneNode *node = new AdSceneNode(_gameRef);
+ if (!region || !node || DID_FAIL(region->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ delete region;
+ delete node;
+ region = NULL;
+ node = NULL;
+ } else {
+ node->setRegion(region);
+ _nodes.add(node);
+ }
+ }
+ break;
+
+ case TOKEN_ENTITY: {
+ AdEntity *entity = new AdEntity(_gameRef);
+ AdSceneNode *node = new AdSceneNode(_gameRef);
+ if (entity) {
+ entity->_zoomable = false; // scene entites default to NOT zoom
+ }
+ if (!entity || !node || DID_FAIL(entity->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ delete entity;
+ delete node;
+ entity = NULL;
+ node = NULL;
+ } else {
+ node->setEntity(entity);
+ _nodes.add(node);
+ }
+ }
+ break;
+
+ case TOKEN_EDITOR_SELECTED:
+ parser.scanStr((char *)params, "%b", &_editorSelected);
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in LAYER definition");
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdLayer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // GetNode
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetNode") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ int node = -1;
+
+ if (val->_type == VAL_INT) {
+ node = val->getInt();
+ } else { // get by name
+ for (uint32 i = 0; i < _nodes.size(); i++) {
+ if ((_nodes[i]->_type == OBJECT_ENTITY && scumm_stricmp(_nodes[i]->_entity->getName(), val->getString()) == 0) ||
+ (_nodes[i]->_type == OBJECT_REGION && scumm_stricmp(_nodes[i]->_region->getName(), val->getString()) == 0)) {
+ node = i;
+ break;
+ }
+ }
+ }
+
+ if (node < 0 || node >= (int32)_nodes.size()) {
+ stack->pushNULL();
+ } else {
+ switch (_nodes[node]->_type) {
+ case OBJECT_ENTITY:
+ stack->pushNative(_nodes[node]->_entity, true);
+ break;
+ case OBJECT_REGION:
+ stack->pushNative(_nodes[node]->_region, true);
+ break;
+ default:
+ stack->pushNULL();
+ }
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddRegion / AddEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddRegion") == 0 || strcmp(name, "AddEntity") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdSceneNode *node = new AdSceneNode(_gameRef);
+ if (strcmp(name, "AddRegion") == 0) {
+ AdRegion *region = new AdRegion(_gameRef);
+ if (!val->isNULL()) {
+ region->setName(val->getString());
+ }
+ node->setRegion(region);
+ stack->pushNative(region, true);
+ } else {
+ AdEntity *entity = new AdEntity(_gameRef);
+ if (!val->isNULL()) {
+ entity->setName(val->getString());
+ }
+ node->setEntity(entity);
+ stack->pushNative(entity, true);
+ }
+ _nodes.add(node);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InsertRegion / InsertEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InsertRegion") == 0 || strcmp(name, "InsertEntity") == 0) {
+ stack->correctParams(2);
+ int index = stack->pop()->getInt();
+ ScValue *val = stack->pop();
+
+ AdSceneNode *node = new AdSceneNode(_gameRef);
+ if (strcmp(name, "InsertRegion") == 0) {
+ AdRegion *region = new AdRegion(_gameRef);
+ if (!val->isNULL()) {
+ region->setName(val->getString());
+ }
+ node->setRegion(region);
+ stack->pushNative(region, true);
+ } else {
+ AdEntity *entity = new AdEntity(_gameRef);
+ if (!val->isNULL()) {
+ entity->setName(val->getString());
+ }
+ node->setEntity(entity);
+ stack->pushNative(entity, true);
+ }
+ if (index < 0) {
+ index = 0;
+ }
+ if (index <= (int32)_nodes.size() - 1) {
+ _nodes.insert_at(index, node);
+ } else {
+ _nodes.add(node);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteNode
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteNode") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdSceneNode *toDelete = NULL;
+ if (val->isNative()) {
+ BaseScriptable *temp = val->getNative();
+ for (uint32 i = 0; i < _nodes.size(); i++) {
+ if (_nodes[i]->_region == temp || _nodes[i]->_entity == temp) {
+ toDelete = _nodes[i];
+ break;
+ }
+ }
+ } else {
+ int index = val->getInt();
+ if (index >= 0 && index < (int32)_nodes.size()) {
+ toDelete = _nodes[index];
+ }
+ }
+ if (toDelete == NULL) {
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ for (uint32 i = 0; i < _nodes.size(); i++) {
+ if (_nodes[i] == toDelete) {
+ delete _nodes[i];
+ _nodes[i] = NULL;
+ _nodes.remove_at(i);
+ break;
+ }
+ }
+ stack->pushBool(true);
+ return STATUS_OK;
+ } else {
+ return BaseObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdLayer::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("layer");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumNodes (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumNodes") {
+ _scValue->setInt(_nodes.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Width
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Width") {
+ _scValue->setInt(_width);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Height
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Height") {
+ _scValue->setInt(_height);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Main (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Main") {
+ _scValue->setBool(_main);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CloseUp
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "CloseUp") {
+ _scValue->setBool(_closeUp);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Active
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Active") {
+ _scValue->setBool(_active);
+ return _scValue;
+ } else {
+ return BaseObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdLayer::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CloseUp
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CloseUp") == 0) {
+ _closeUp = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Width
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Width") == 0) {
+ _width = value->getInt();
+ if (_width < 0) {
+ _width = 0;
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Height
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Height") == 0) {
+ _height = value->getInt();
+ if (_height < 0) {
+ _height = 0;
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Active
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Active") == 0) {
+ bool b = value->getBool();
+ if (b == false && _main) {
+ _gameRef->LOG(0, "Warning: cannot deactivate scene's main layer");
+ } else {
+ _active = b;
+ }
+ return STATUS_OK;
+ } else {
+ return BaseObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdLayer::scToString() {
+ return "[layer]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdLayer::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "LAYER {\n");
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+ buffer->putTextIndent(indent + 2, "MAIN=%s\n", _main ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width);
+ buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height);
+ buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE");
+ if (_closeUp) {
+ buffer->putTextIndent(indent + 2, "CLOSE_UP=%s\n", _closeUp ? "TRUE" : "FALSE");
+ }
+
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ if (_scProp) {
+ _scProp->saveAsText(buffer, indent + 2);
+ }
+
+ for (uint32 i = 0; i < _nodes.size(); i++) {
+ switch (_nodes[i]->_type) {
+ case OBJECT_ENTITY:
+ _nodes[i]->_entity->saveAsText(buffer, indent + 2);
+ break;
+ case OBJECT_REGION:
+ _nodes[i]->_region->saveAsText(buffer, indent + 2);
+ break;
+ default:
+ error("AdLayer::SaveAsText - Unhandled enum");
+ break;
+ }
+ }
+
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdLayer::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_active));
+ persistMgr->transfer(TMEMBER(_closeUp));
+ persistMgr->transfer(TMEMBER(_height));
+ persistMgr->transfer(TMEMBER(_main));
+ _nodes.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_width));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_layer.h b/engines/wintermute/ad/ad_layer.h
new file mode 100644
index 0000000000..de65e2822f
--- /dev/null
+++ b/engines/wintermute/ad/ad_layer.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADLAYER_H
+#define WINTERMUTE_ADLAYER_H
+
+namespace Wintermute {
+class AdSceneNode;
+class AdLayer : public BaseObject {
+public:
+ bool _closeUp;
+ DECLARE_PERSISTENT(AdLayer, BaseObject)
+ bool _active;
+ int _height;
+ int _width;
+ bool _main;
+ AdLayer(BaseGame *inGame);
+ virtual ~AdLayer();
+ BaseArray<AdSceneNode *> _nodes;
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_node_state.cpp b/engines/wintermute/ad/ad_node_state.cpp
new file mode 100644
index 0000000000..493156c750
--- /dev/null
+++ b/engines/wintermute/ad/ad_node_state.cpp
@@ -0,0 +1,196 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/ad/ad_node_state.h"
+#include "engines/wintermute/ad/ad_entity.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdNodeState, false)
+
+
+//////////////////////////////////////////////////////////////////////////
+AdNodeState::AdNodeState(BaseGame *inGame) : BaseClass(inGame) {
+ _name = NULL;
+ _active = false;
+ for (int i = 0; i < 7; i++) {
+ _caption[i] = NULL;
+ }
+ _alphaColor = 0;
+ _filename = NULL;
+ _cursor = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdNodeState::~AdNodeState() {
+ delete[] _name;
+ delete[] _filename;
+ delete[] _cursor;
+ _name = NULL;
+ _filename = NULL;
+ _cursor = NULL;
+ for (int i = 0; i < 7; i++) {
+ delete[] _caption[i];
+ _caption[i] = NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdNodeState::setName(const char *name) {
+ delete[] _name;
+ _name = NULL;
+ BaseUtils::setString(&_name, name);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdNodeState::setFilename(const char *filename) {
+ delete[] _filename;
+ _filename = NULL;
+ BaseUtils::setString(&_filename, filename);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdNodeState::setCursor(const char *filename) {
+ delete[] _cursor;
+ _cursor = NULL;
+ BaseUtils::setString(&_cursor, filename);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdNodeState::persist(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER(_active));
+ persistMgr->transfer(TMEMBER(_name));
+ persistMgr->transfer(TMEMBER(_filename));
+ persistMgr->transfer(TMEMBER(_cursor));
+ persistMgr->transfer(TMEMBER(_alphaColor));
+ for (int i = 0; i < 7; i++) {
+ persistMgr->transfer(TMEMBER(_caption[i]));
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdNodeState::setCaption(const char *caption, int caseVal) {
+ if (caseVal == 0) {
+ caseVal = 1;
+ }
+ if (caseVal < 1 || caseVal > 7) {
+ return;
+ }
+
+ delete[] _caption[caseVal - 1];
+ _caption[caseVal - 1] = new char[strlen(caption) + 1];
+ if (_caption[caseVal - 1]) {
+ strcpy(_caption[caseVal - 1], caption);
+ _gameRef->_stringTable->expand(&_caption[caseVal - 1]);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdNodeState::getCaption(int caseVal) {
+ if (caseVal == 0) {
+ caseVal = 1;
+ }
+ if (caseVal < 1 || caseVal > 7 || _caption[caseVal - 1] == NULL) {
+ return "";
+ } else {
+ return _caption[caseVal - 1];
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdNodeState::transferEntity(AdEntity *entity, bool includingSprites, bool saving) {
+ if (!entity) {
+ return STATUS_FAILED;
+ }
+
+ // HACK!
+ if (this->_gameRef != entity->_gameRef) {
+ this->_gameRef = entity->_gameRef;
+ }
+
+ if (saving) {
+ for (int i = 0; i < 7; i++) {
+ if (entity->_caption[i]) {
+ setCaption(entity->_caption[i], i);
+ }
+ }
+ if (!entity->_region && entity->_sprite && entity->_sprite->getFilename()) {
+ if (includingSprites) {
+ setFilename(entity->_sprite->getFilename());
+ } else {
+ setFilename("");
+ }
+ }
+ if (entity->_cursor && entity->_cursor->getFilename()) {
+ setCursor(entity->_cursor->getFilename());
+ }
+ _alphaColor = entity->_alphaColor;
+ _active = entity->_active;
+ } else {
+ for (int i = 0; i < 7; i++) {
+ if (_caption[i]) {
+ entity->setCaption(_caption[i], i);
+ }
+ }
+ if (_filename && !entity->_region && includingSprites && strcmp(_filename, "") != 0) {
+ if (!entity->_sprite || !entity->_sprite->getFilename() || scumm_stricmp(entity->_sprite->getFilename(), _filename) != 0) {
+ entity->setSprite(_filename);
+ }
+ }
+ if (_cursor) {
+ if (!entity->_cursor || !entity->_cursor->getFilename() || scumm_stricmp(entity->_cursor->getFilename(), _cursor) != 0) {
+ entity->setCursor(_cursor);
+ }
+ }
+
+ entity->_active = _active;
+ entity->_alphaColor = _alphaColor;
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_node_state.h b/engines/wintermute/ad/ad_node_state.h
new file mode 100644
index 0000000000..e2050815a7
--- /dev/null
+++ b/engines/wintermute/ad/ad_node_state.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADNODESTATE_H
+#define WINTERMUTE_ADNODESTATE_H
+
+namespace Wintermute {
+
+class AdEntity;
+
+class AdNodeState : public BaseClass {
+public:
+ bool _active;
+ bool transferEntity(AdEntity *entity, bool includingSprites, bool saving);
+ void setName(const char *name);
+ void setFilename(const char *filename);
+ void setCursor(const char *filename);
+ DECLARE_PERSISTENT(AdNodeState, BaseClass)
+ AdNodeState(BaseGame *inGame);
+ virtual ~AdNodeState();
+ const char *getName() const { return _name; }
+private:
+ char *_name;
+ char *_caption[7];
+ void setCaption(const char *caption, int caseVal);
+ const char *getCaption(int caseVal);
+ uint32 _alphaColor;
+ char *_filename;
+ char *_cursor;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_object.cpp b/engines/wintermute/ad/ad_object.cpp
new file mode 100644
index 0000000000..7b91daab2e
--- /dev/null
+++ b/engines/wintermute/ad/ad_object.cpp
@@ -0,0 +1,1300 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_item.h"
+#include "engines/wintermute/ad/ad_object.h"
+#include "engines/wintermute/ad/ad_inventory.h"
+#include "engines/wintermute/ad/ad_layer.h"
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/ad/ad_scene_node.h"
+#include "engines/wintermute/ad/ad_sentence.h"
+#include "engines/wintermute/ad/ad_waypoint_group.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_frame.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/base_surface_storage.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/particles/part_emitter.h"
+#include "engines/wintermute/base/scriptables/script_engine.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "common/str.h"
+#include "common/util.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdObject, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdObject::AdObject(BaseGame *inGame) : BaseObject(inGame) {
+ _type = OBJECT_NONE;
+ _state = _nextState = STATE_NONE;
+
+ _active = true;
+ _drawn = false;
+
+ _currentSprite = NULL;
+ _animSprite = NULL;
+ _tempSprite2 = NULL;
+
+ _font = NULL;
+
+ _sentence = NULL;
+
+ _forcedTalkAnimName = NULL;
+ _forcedTalkAnimUsed = false;
+
+ _blockRegion = NULL;
+ _wptGroup = NULL;
+
+ _currentBlockRegion = NULL;
+ _currentWptGroup = NULL;
+
+ _ignoreItems = false;
+ _sceneIndependent = false;
+
+ _stickRegion = NULL;
+
+ _subtitlesModRelative = true;
+ _subtitlesModX = 0;
+ _subtitlesModY = 0;
+ _subtitlesWidth = 0;
+ _subtitlesModXCenter = true;
+
+ _inventory = NULL;
+
+ for (int i = 0; i < MAX_NUM_REGIONS; i++) {
+ _currentRegions[i] = NULL;
+ }
+
+ _partEmitter = NULL;
+ _partFollowParent = false;
+ _partOffsetX = _partOffsetY = 0;
+
+ _registerAlias = this;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdObject::~AdObject() {
+ _currentSprite = NULL; // reference only, don't delete
+ delete _animSprite;
+ _animSprite = NULL;
+ delete _sentence;
+ _sentence = NULL;
+ delete[] _forcedTalkAnimName;
+ _forcedTalkAnimName = NULL;
+
+ delete _blockRegion;
+ _blockRegion = NULL;
+ delete _wptGroup;
+ _wptGroup = NULL;
+
+ delete _currentBlockRegion;
+ _currentBlockRegion = NULL;
+ delete _currentWptGroup;
+ _currentWptGroup = NULL;
+
+ _tempSprite2 = NULL; // reference only
+ _stickRegion = NULL;
+
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+
+ if (_inventory) {
+ ((AdGame *)_gameRef)->unregisterInventory(_inventory);
+ _inventory = NULL;
+ }
+
+ if (_partEmitter) {
+ _gameRef->unregisterObject(_partEmitter);
+ }
+
+
+ for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
+ _gameRef->unregisterObject(_attachmentsPre[i]);
+ }
+ _attachmentsPre.clear();
+
+ for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
+ _gameRef->unregisterObject(_attachmentsPost[i]);
+ }
+ _attachmentsPost.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::playAnim(const char *filename) {
+ delete _animSprite;
+ _animSprite = NULL;
+ _animSprite = new BaseSprite(_gameRef, this);
+ if (!_animSprite) {
+ _gameRef->LOG(0, "AdObject::PlayAnim: error creating temp sprite (object:\"%s\" sprite:\"%s\")", getName(), filename);
+ return STATUS_FAILED;
+ }
+ bool res = _animSprite->loadFile(filename);
+ if (DID_FAIL(res)) {
+ _gameRef->LOG(res, "AdObject::PlayAnim: error loading temp sprite (object:\"%s\" sprite:\"%s\")", getName(), filename);
+ delete _animSprite;
+ _animSprite = NULL;
+ return res;
+ }
+ _state = STATE_PLAYING_ANIM;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::display() {
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::update() {
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // PlayAnim / PlayAnimAsync
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "PlayAnim") == 0 || strcmp(name, "PlayAnimAsync") == 0) {
+ stack->correctParams(1);
+ if (DID_FAIL(playAnim(stack->pop()->getString()))) {
+ stack->pushBool(false);
+ } else {
+ if (strcmp(name, "PlayAnimAsync") != 0) {
+ script->waitFor(this);
+ }
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Reset
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Reset") == 0) {
+ stack->correctParams(0);
+ reset();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsTalking
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsTalking") == 0) {
+ stack->correctParams(0);
+ stack->pushBool(_state == STATE_TALKING);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // StopTalk / StopTalking
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "StopTalk") == 0 || strcmp(name, "StopTalking") == 0) {
+ stack->correctParams(0);
+ if (_sentence) {
+ _sentence->finish();
+ }
+ if (_state == STATE_TALKING) {
+ _state = _nextState;
+ _nextState = STATE_READY;
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ForceTalkAnim
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ForceTalkAnim") == 0) {
+ stack->correctParams(1);
+ const char *animName = stack->pop()->getString();
+ delete[] _forcedTalkAnimName;
+ _forcedTalkAnimName = new char[strlen(animName) + 1];
+ strcpy(_forcedTalkAnimName, animName);
+ _forcedTalkAnimUsed = false;
+ stack->pushBool(true);
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Talk / TalkAsync
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Talk") == 0 || strcmp(name, "TalkAsync") == 0) {
+ stack->correctParams(5);
+
+ const char *text = stack->pop()->getString();
+ ScValue *soundVal = stack->pop();
+ int duration = stack->pop()->getInt();
+ ScValue *valStances = stack->pop();
+
+ const char *stances = valStances->isNULL() ? NULL : valStances->getString();
+
+ int align = 0;
+ ScValue *val = stack->pop();
+ if (val->isNULL()) {
+ align = TAL_CENTER;
+ } else {
+ align = val->getInt();
+ }
+
+ align = MIN(MAX(0, align), NUM_TEXT_ALIGN - 1);
+
+ const char *sound = soundVal->isNULL() ? NULL : soundVal->getString();
+
+ talk(text, sound, duration, stances, (TTextAlign)align);
+ if (strcmp(name, "TalkAsync") != 0) {
+ script->waitForExclusive(this);
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // StickToRegion
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "StickToRegion") == 0) {
+ stack->correctParams(1);
+
+ AdLayer *main = ((AdGame *)_gameRef)->_scene->_mainLayer;
+ bool regFound = false;
+
+ uint32 i;
+ ScValue *val = stack->pop();
+ if (val->isNULL() || !main) {
+ _stickRegion = NULL;
+ regFound = true;
+ } else if (val->isString()) {
+ const char *regionName = val->getString();
+ for (i = 0; i < main->_nodes.size(); i++) {
+ if (main->_nodes[i]->_type == OBJECT_REGION && main->_nodes[i]->_region->getName() && scumm_stricmp(main->_nodes[i]->_region->getName(), regionName) == 0) {
+ _stickRegion = main->_nodes[i]->_region;
+ regFound = true;
+ break;
+ }
+ }
+ } else if (val->isNative()) {
+ BaseScriptable *obj = val->getNative();
+
+ for (i = 0; i < main->_nodes.size(); i++) {
+ if (main->_nodes[i]->_type == OBJECT_REGION && main->_nodes[i]->_region == obj) {
+ _stickRegion = main->_nodes[i]->_region;
+ regFound = true;
+ break;
+ }
+ }
+
+ }
+
+ if (!regFound) {
+ _stickRegion = NULL;
+ }
+ stack->pushBool(regFound);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetFont
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetFont") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ if (val->isNULL()) {
+ setFont(NULL);
+ } else {
+ setFont(val->getString());
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFont
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFont") == 0) {
+ stack->correctParams(0);
+ if (_font && _font->getFilename()) {
+ stack->pushString(_font->getFilename());
+ } else {
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TakeItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TakeItem") == 0) {
+ stack->correctParams(2);
+
+ if (!_inventory) {
+ _inventory = new AdInventory(_gameRef);
+ ((AdGame *)_gameRef)->registerInventory(_inventory);
+ }
+
+ ScValue *val = stack->pop();
+ if (!val->isNULL()) {
+ const char *itemName = val->getString();
+ val = stack->pop();
+ const char *insertAfter = val->isNULL() ? NULL : val->getString();
+ if (DID_FAIL(_inventory->insertItem(itemName, insertAfter))) {
+ script->runtimeError("Cannot add item '%s' to inventory", itemName);
+ } else {
+ // hide associated entities
+ ((AdGame *)_gameRef)->_scene->handleItemAssociations(itemName, false);
+ }
+
+ } else {
+ script->runtimeError("TakeItem: item name expected");
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DropItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DropItem") == 0) {
+ stack->correctParams(1);
+
+ if (!_inventory) {
+ _inventory = new AdInventory(_gameRef);
+ ((AdGame *)_gameRef)->registerInventory(_inventory);
+ }
+
+ ScValue *val = stack->pop();
+ if (!val->isNULL()) {
+ if (DID_FAIL(_inventory->removeItem(val->getString()))) {
+ script->runtimeError("Cannot remove item '%s' from inventory", val->getString());
+ } else {
+ // show associated entities
+ ((AdGame *)_gameRef)->_scene->handleItemAssociations(val->getString(), true);
+ }
+ } else {
+ script->runtimeError("DropItem: item name expected");
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetItem") == 0) {
+ stack->correctParams(1);
+
+ if (!_inventory) {
+ _inventory = new AdInventory(_gameRef);
+ ((AdGame *)_gameRef)->registerInventory(_inventory);
+ }
+
+ ScValue *val = stack->pop();
+ if (val->_type == VAL_STRING) {
+ AdItem *item = ((AdGame *)_gameRef)->getItemByName(val->getString());
+ if (item) {
+ stack->pushNative(item, true);
+ } else {
+ stack->pushNULL();
+ }
+ } else if (val->isNULL() || val->getInt() < 0 || val->getInt() >= (int32)_inventory->_takenItems.size()) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_inventory->_takenItems[val->getInt()], true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HasItem
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HasItem") == 0) {
+ stack->correctParams(1);
+
+ if (!_inventory) {
+ _inventory = new AdInventory(_gameRef);
+ ((AdGame *)_gameRef)->registerInventory(_inventory);
+ }
+
+ ScValue *val = stack->pop();
+ if (!val->isNULL()) {
+ for (uint32 i = 0; i < _inventory->_takenItems.size(); i++) {
+ if (val->getNative() == _inventory->_takenItems[i]) {
+ stack->pushBool(true);
+ return STATUS_OK;
+ } else if (scumm_stricmp(val->getString(), _inventory->_takenItems[i]->getName()) == 0) {
+ stack->pushBool(true);
+ return STATUS_OK;
+ }
+ }
+ } else {
+ script->runtimeError("HasItem: item name expected");
+ }
+
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateParticleEmitter
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateParticleEmitter") == 0) {
+ stack->correctParams(3);
+ bool followParent = stack->pop()->getBool();
+ int offsetX = stack->pop()->getInt();
+ int offsetY = stack->pop()->getInt();
+
+ PartEmitter *emitter = createParticleEmitter(followParent, offsetX, offsetY);
+ if (emitter) {
+ stack->pushNative(_partEmitter, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteParticleEmitter
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteParticleEmitter") == 0) {
+ stack->correctParams(0);
+ if (_partEmitter) {
+ _gameRef->unregisterObject(_partEmitter);
+ _partEmitter = NULL;
+ }
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddAttachment
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddAttachment") == 0) {
+ stack->correctParams(4);
+ const char *filename = stack->pop()->getString();
+ bool preDisplay = stack->pop()->getBool(true);
+ int offsetX = stack->pop()->getInt();
+ int offsetY = stack->pop()->getInt();
+
+ bool res;
+ AdEntity *ent = new AdEntity(_gameRef);
+ if (DID_FAIL(res = ent->loadFile(filename))) {
+ delete ent;
+ ent = NULL;
+ script->runtimeError("AddAttachment() failed loading entity '%s'", filename);
+ stack->pushBool(false);
+ } else {
+ _gameRef->registerObject(ent);
+
+ ent->_posX = offsetX;
+ ent->_posY = offsetY;
+ ent->_active = true;
+
+ if (preDisplay) {
+ _attachmentsPre.add(ent);
+ } else {
+ _attachmentsPost.add(ent);
+ }
+
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RemoveAttachment
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemoveAttachment") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ bool found = false;
+ if (val->isNative()) {
+ BaseScriptable *obj = val->getNative();
+ for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
+ if (_attachmentsPre[i] == obj) {
+ found = true;
+ _gameRef->unregisterObject(_attachmentsPre[i]);
+ _attachmentsPre.remove_at(i);
+ i--;
+ }
+ }
+ for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
+ if (_attachmentsPost[i] == obj) {
+ found = true;
+ _gameRef->unregisterObject(_attachmentsPost[i]);
+ _attachmentsPost.remove_at(i);
+ i--;
+ }
+ }
+ } else {
+ const char *attachmentName = val->getString();
+ for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
+ if (_attachmentsPre[i]->getName() && scumm_stricmp(_attachmentsPre[i]->getName(), attachmentName) == 0) {
+ found = true;
+ _gameRef->unregisterObject(_attachmentsPre[i]);
+ _attachmentsPre.remove_at(i);
+ i--;
+ }
+ }
+ for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
+ if (_attachmentsPost[i]->getName() && scumm_stricmp(_attachmentsPost[i]->getName(), attachmentName) == 0) {
+ found = true;
+ _gameRef->unregisterObject(_attachmentsPost[i]);
+ _attachmentsPost.remove_at(i);
+ i--;
+ }
+ }
+ }
+ stack->pushBool(found);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetAttachment
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetAttachment") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdObject *ret = NULL;
+ if (val->isInt()) {
+ int index = val->getInt();
+ int currIndex = 0;
+ for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
+ if (currIndex == index) {
+ ret = _attachmentsPre[i];
+ }
+ currIndex++;
+ }
+ for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
+ if (currIndex == index) {
+ ret = _attachmentsPost[i];
+ }
+ currIndex++;
+ }
+ } else {
+ const char *attachmentName = val->getString();
+ for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
+ if (_attachmentsPre[i]->getName() && scumm_stricmp(_attachmentsPre[i]->getName(), attachmentName) == 0) {
+ ret = _attachmentsPre[i];
+ break;
+ }
+ }
+ if (!ret) {
+ for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
+ if (_attachmentsPost[i]->getName() && scumm_stricmp(_attachmentsPost[i]->getName(), attachmentName) == 0) {
+ ret = _attachmentsPre[i];
+ break;
+ }
+ }
+ }
+ }
+
+ if (ret != NULL) {
+ stack->pushNative(ret, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ } else {
+ return BaseObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdObject::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("object");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Active
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Active") {
+ _scValue->setBool(_active);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IgnoreItems
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "IgnoreItems") {
+ _scValue->setBool(_ignoreItems);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SceneIndependent
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SceneIndependent") {
+ _scValue->setBool(_sceneIndependent);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesWidth
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SubtitlesWidth") {
+ _scValue->setInt(_subtitlesWidth);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesPosRelative
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SubtitlesPosRelative") {
+ _scValue->setBool(_subtitlesModRelative);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesPosX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SubtitlesPosX") {
+ _scValue->setInt(_subtitlesModX);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesPosY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SubtitlesPosY") {
+ _scValue->setInt(_subtitlesModY);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesPosXCenter
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SubtitlesPosXCenter") {
+ _scValue->setBool(_subtitlesModXCenter);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumItems (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumItems") {
+ _scValue->setInt(getInventory()->_takenItems.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ParticleEmitter (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ParticleEmitter") {
+ if (_partEmitter) {
+ _scValue->setNative(_partEmitter, true);
+ } else {
+ _scValue->setNULL();
+ }
+
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumAttachments (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumAttachments") {
+ _scValue->setInt(_attachmentsPre.size() + _attachmentsPost.size());
+ return _scValue;
+ } else {
+ return BaseObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::scSetProperty(const char *name, ScValue *value) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Active
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Active") == 0) {
+ _active = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IgnoreItems
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IgnoreItems") == 0) {
+ _ignoreItems = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SceneIndependent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SceneIndependent") == 0) {
+ _sceneIndependent = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesWidth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SubtitlesWidth") == 0) {
+ _subtitlesWidth = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesPosRelative
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SubtitlesPosRelative") == 0) {
+ _subtitlesModRelative = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesPosX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SubtitlesPosX") == 0) {
+ _subtitlesModX = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesPosY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SubtitlesPosY") == 0) {
+ _subtitlesModY = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesPosXCenter
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SubtitlesPosXCenter") == 0) {
+ _subtitlesModXCenter = value->getBool();
+ return STATUS_OK;
+ } else {
+ return BaseObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdObject::scToString() {
+ return "[ad object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::setFont(const char *filename) {
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ if (filename) {
+ _font = _gameRef->_fontStorage->addFont(filename);
+ return _font == NULL ? STATUS_FAILED : STATUS_OK;
+ } else {
+ _font = NULL;
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int AdObject::getHeight() {
+ if (!_currentSprite) {
+ return 0;
+ } else {
+ BaseFrame *frame = _currentSprite->_frames[_currentSprite->_currentFrame];
+ int ret = 0;
+ for (uint32 i = 0; i < frame->_subframes.size(); i++) {
+ ret = MAX(ret, frame->_subframes[i]->_hotspotY);
+ }
+
+ if (_zoomable) {
+ float zoom = ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY);
+ ret = (int)(ret * zoom / 100);
+ }
+ return ret;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdObject::talk(const char *text, const char *sound, uint32 duration, const char *stances, TTextAlign Align) {
+ if (!_sentence) {
+ _sentence = new AdSentence(_gameRef);
+ }
+ if (!_sentence) {
+ return;
+ }
+
+ if (_forcedTalkAnimName && _forcedTalkAnimUsed) {
+ delete[] _forcedTalkAnimName;
+ _forcedTalkAnimName = NULL;
+ _forcedTalkAnimUsed = false;
+ }
+
+ delete(_sentence->_sound);
+ _sentence->_sound = NULL;
+
+ _sentence->setText(text);
+ _gameRef->_stringTable->expand(&_sentence->_text);
+ _sentence->setStances(stances);
+ _sentence->_duration = duration;
+ _sentence->_align = Align;
+ _sentence->_startTime = _gameRef->_timer;
+ _sentence->_currentStance = -1;
+ _sentence->_font = _font == NULL ? _gameRef->_systemFont : _font;
+ _sentence->_freezable = _freezable;
+
+ // try to locate speech file automatically
+ bool deleteSound = false;
+ if (!sound) {
+ char *key = _gameRef->_stringTable->getKey(text);
+ if (key) {
+ sound = ((AdGame *)_gameRef)->findSpeechFile(key);
+ delete[] key;
+
+ if (sound) {
+ deleteSound = true;
+ }
+ }
+ }
+
+ // load sound and set duration appropriately
+ if (sound) {
+ BaseSound *snd = new BaseSound(_gameRef);
+ if (snd && DID_SUCCEED(snd->setSound(sound, Audio::Mixer::kSpeechSoundType, true))) {
+ _sentence->setSound(snd);
+ if (_sentence->_duration <= 0) {
+ uint32 length = snd->getLength();
+ if (length != 0) {
+ _sentence->_duration = length;
+ }
+ }
+ } else {
+ delete snd;
+ }
+ }
+
+ // set duration by text length
+ if (_sentence->_duration <= 0) {// TODO: Avoid longs.
+ _sentence->_duration = MAX((size_t)1000, _gameRef->_subtitlesSpeed * strlen(_sentence->_text));
+ }
+
+
+ int x, y, width, height;
+
+ x = _posX;
+ y = _posY;
+
+ if (!_sceneIndependent && _subtitlesModRelative) {
+ x -= ((AdGame *)_gameRef)->_scene->getOffsetLeft();
+ y -= ((AdGame *)_gameRef)->_scene->getOffsetTop();
+ }
+
+
+ if (_subtitlesWidth > 0) {
+ width = _subtitlesWidth;
+ } else {
+ if ((x < _gameRef->_renderer->_width / 4 || x > _gameRef->_renderer->_width * 0.75) && !_gameRef->_touchInterface) {
+ width = MAX(_gameRef->_renderer->_width / 4, MIN(x * 2, (_gameRef->_renderer->_width - x) * 2));
+ } else {
+ width = _gameRef->_renderer->_width / 2;
+ }
+ }
+
+ height = _sentence->_font->getTextHeight((byte *)_sentence->_text, width);
+
+ y = y - height - getHeight() - 5;
+ if (_subtitlesModRelative) {
+ x += _subtitlesModX;
+ y += _subtitlesModY;
+ } else {
+ x = _subtitlesModX;
+ y = _subtitlesModY;
+ }
+ if (_subtitlesModXCenter) {
+ x = x - width / 2;
+ }
+
+
+ x = MIN(MAX(0, x), _gameRef->_renderer->_width - width);
+ y = MIN(MAX(0, y), _gameRef->_renderer->_height - height);
+
+ _sentence->_width = width;
+
+
+ _sentence->_pos.x = x;
+ _sentence->_pos.y = y;
+
+
+ if (_subtitlesModRelative) {
+ _sentence->_pos.x += ((AdGame *)_gameRef)->_scene->getOffsetLeft();
+ _sentence->_pos.y += ((AdGame *)_gameRef)->_scene->getOffsetTop();
+ }
+
+ _sentence->_fixedPos = !_subtitlesModRelative;
+
+
+ _sentence->setupTalkFile(sound);
+
+ _state = STATE_TALKING;
+
+ if (deleteSound) {
+ delete[] sound;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::reset() {
+ if (_state == STATE_PLAYING_ANIM && _animSprite != NULL) {
+ delete _animSprite;
+ _animSprite = NULL;
+ } else if (_state == STATE_TALKING && _sentence) {
+ _sentence->finish();
+ }
+
+ _state = _nextState = STATE_READY;
+
+ _gameRef->_scEngine->resetObject(this);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::persist(BasePersistenceManager *persistMgr) {
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_active));
+ persistMgr->transfer(TMEMBER(_blockRegion));
+ persistMgr->transfer(TMEMBER(_currentBlockRegion));
+ persistMgr->transfer(TMEMBER(_currentWptGroup));
+ persistMgr->transfer(TMEMBER(_currentSprite));
+ persistMgr->transfer(TMEMBER(_drawn));
+ persistMgr->transfer(TMEMBER(_font));
+ persistMgr->transfer(TMEMBER(_ignoreItems));
+ persistMgr->transfer(TMEMBER_INT(_nextState));
+ persistMgr->transfer(TMEMBER(_sentence));
+ persistMgr->transfer(TMEMBER_INT(_state));
+ persistMgr->transfer(TMEMBER(_animSprite));
+ persistMgr->transfer(TMEMBER(_sceneIndependent));
+ persistMgr->transfer(TMEMBER(_forcedTalkAnimName));
+ persistMgr->transfer(TMEMBER(_forcedTalkAnimUsed));
+ persistMgr->transfer(TMEMBER(_tempSprite2));
+ persistMgr->transfer(TMEMBER_INT(_type));
+ persistMgr->transfer(TMEMBER(_wptGroup));
+ persistMgr->transfer(TMEMBER(_stickRegion));
+ persistMgr->transfer(TMEMBER(_subtitlesModRelative));
+ persistMgr->transfer(TMEMBER(_subtitlesModX));
+ persistMgr->transfer(TMEMBER(_subtitlesModY));
+ persistMgr->transfer(TMEMBER(_subtitlesModXCenter));
+ persistMgr->transfer(TMEMBER(_subtitlesWidth));
+ persistMgr->transfer(TMEMBER(_inventory));
+ persistMgr->transfer(TMEMBER(_partEmitter));
+
+ for (int i = 0; i < MAX_NUM_REGIONS; i++) {
+ persistMgr->transfer(TMEMBER(_currentRegions[i]));
+ }
+
+ _attachmentsPre.persist(persistMgr);
+ _attachmentsPost.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_registerAlias));
+
+ persistMgr->transfer(TMEMBER(_partFollowParent));
+ persistMgr->transfer(TMEMBER(_partOffsetX));
+ persistMgr->transfer(TMEMBER(_partOffsetY));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::updateSounds() {
+ if (_sentence && _sentence->_sound) {
+ updateOneSound(_sentence->_sound);
+ }
+
+ return BaseObject::updateSounds();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::resetSoundPan() {
+ if (_sentence && _sentence->_sound) {
+ _sentence->_sound->setPan(0.0f);
+ }
+ return BaseObject::resetSoundPan();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::getExtendedFlag(const char *flagName) {
+ if (!flagName) {
+ return false;
+ } else if (strcmp(flagName, "usable") == 0) {
+ return true;
+ } else {
+ return BaseObject::getExtendedFlag(flagName);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ if (_blockRegion) {
+ _blockRegion->saveAsText(buffer, indent + 2, "BLOCKED_REGION");
+ }
+ if (_wptGroup) {
+ _wptGroup->saveAsText(buffer, indent + 2);
+ }
+
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::updateBlockRegion() {
+ AdGame *adGame = (AdGame *)_gameRef;
+ if (adGame->_scene) {
+ if (_blockRegion && _currentBlockRegion) {
+ _currentBlockRegion->mimic(_blockRegion, _zoomable ? adGame->_scene->getScaleAt(_posY) : 100.0f, _posX, _posY);
+ }
+
+ if (_wptGroup && _currentWptGroup) {
+ _currentWptGroup->mimic(_wptGroup, _zoomable ? adGame->_scene->getScaleAt(_posY) : 100.0f, _posX, _posY);
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AdInventory *AdObject::getInventory() {
+ if (!_inventory) {
+ _inventory = new AdInventory(_gameRef);
+ ((AdGame *)_gameRef)->registerInventory(_inventory);
+ }
+ return _inventory;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::afterMove() {
+ AdRegion *newRegions[MAX_NUM_REGIONS];
+
+ ((AdGame *)_gameRef)->_scene->getRegionsAt(_posX, _posY, newRegions, MAX_NUM_REGIONS);
+ for (int i = 0; i < MAX_NUM_REGIONS; i++) {
+ if (!newRegions[i]) {
+ break;
+ }
+ bool regFound = false;
+ for (int j = 0; j < MAX_NUM_REGIONS; j++) {
+ if (_currentRegions[j] == newRegions[i]) {
+ _currentRegions[j] = NULL;
+ regFound = true;
+ break;
+ }
+ }
+ if (!regFound) {
+ newRegions[i]->applyEvent("ActorEntry");
+ }
+ }
+
+ for (int i = 0; i < MAX_NUM_REGIONS; i++) {
+ if (_currentRegions[i] && _gameRef->validObject(_currentRegions[i])) {
+ _currentRegions[i]->applyEvent("ActorLeave");
+ }
+ _currentRegions[i] = newRegions[i];
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::invalidateCurrRegions() {
+ for (int i = 0; i < MAX_NUM_REGIONS; i++) {
+ _currentRegions[i] = NULL;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::getScale(float *scaleX, float *scaleY) {
+ if (_zoomable) {
+ if (_scaleX >= 0 || _scaleY >= 0) {
+ *scaleX = _scaleX < 0 ? 100 : _scaleX;
+ *scaleY = _scaleY < 0 ? 100 : _scaleY;
+ } else if (_scale >= 0) {
+ *scaleX = *scaleY = _scale;
+ } else {
+ *scaleX = *scaleY = ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) + _relativeScale;
+ }
+ } else {
+ *scaleX = *scaleY = 100;
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::updateSpriteAttachments() {
+ for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
+ _attachmentsPre[i]->update();
+ }
+ for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
+ _attachmentsPost[i]->update();
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::displaySpriteAttachments(bool preDisplay) {
+ if (preDisplay) {
+ for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
+ displaySpriteAttachment(_attachmentsPre[i]);
+ }
+ } else {
+ for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
+ displaySpriteAttachment(_attachmentsPost[i]);
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::displaySpriteAttachment(AdObject *attachment) {
+ if (!attachment->_active) {
+ return STATUS_OK;
+ }
+
+ float scaleX, scaleY;
+ getScale(&scaleX, &scaleY);
+
+ int origX = attachment->_posX;
+ int origY = attachment->_posY;
+
+ // inherit position from owner
+ attachment->_posX = (int)(this->_posX + attachment->_posX * scaleX / 100.0f);
+ attachment->_posY = (int)(this->_posY + attachment->_posY * scaleY / 100.0f);
+
+ // inherit other props
+ attachment->_alphaColor = this->_alphaColor;
+ attachment->_blendMode = this->_blendMode;
+
+ attachment->_scale = this->_scale;
+ attachment->_relativeScale = this->_relativeScale;
+ attachment->_scaleX = this->_scaleX;
+ attachment->_scaleY = this->_scaleY;
+
+ attachment->_rotate = this->_rotate;
+ attachment->_relativeRotate = this->_relativeRotate;
+ attachment->_rotateValid = this->_rotateValid;
+
+ attachment->_registerAlias = this;
+ attachment->_registrable = this->_registrable;
+
+ bool ret = attachment->display();
+
+ attachment->_posX = origX;
+ attachment->_posY = origY;
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+PartEmitter *AdObject::createParticleEmitter(bool followParent, int offsetX, int offsetY) {
+ _partFollowParent = followParent;
+ _partOffsetX = offsetX;
+ _partOffsetY = offsetY;
+
+ if (!_partEmitter) {
+ _partEmitter = new PartEmitter(_gameRef, this);
+ if (_partEmitter) {
+ _gameRef->registerObject(_partEmitter);
+ }
+ }
+ updatePartEmitter();
+ return _partEmitter;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdObject::updatePartEmitter() {
+ if (!_partEmitter) {
+ return STATUS_FAILED;
+ }
+
+ if (_partFollowParent) {
+ float scaleX, scaleY;
+ getScale(&scaleX, &scaleY);
+
+ _partEmitter->_posX = (int)(_posX + (scaleX / 100.0f) * _partOffsetX);
+ _partEmitter->_posY = (int)(_posY + (scaleY / 100.0f) * _partOffsetY);
+ }
+ return _partEmitter->update();
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_object.h b/engines/wintermute/ad/ad_object.h
new file mode 100644
index 0000000000..d1a20908e1
--- /dev/null
+++ b/engines/wintermute/ad/ad_object.h
@@ -0,0 +1,124 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADOBJECT_H
+#define WINTERMUTE_ADOBJECT_H
+
+#include "engines/wintermute/ad/ad_types.h"
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+
+class AdWaypointGroup;
+class AdRegion;
+class AdSentence;
+class BaseFont;
+class BaseRegion;
+class AdInventory;
+class PartEmitter;
+
+#define MAX_NUM_REGIONS 10
+
+class AdObject : public BaseObject {
+public:
+ PartEmitter *_partEmitter;
+ virtual PartEmitter *createParticleEmitter(bool followParent = false, int offsetX = 0, int offsetY = 0);
+ virtual bool updatePartEmitter();
+ bool _partFollowParent;
+ int _partOffsetX;
+ int _partOffsetY;
+
+ bool invalidateCurrRegions();
+ bool _subtitlesModRelative;
+ bool _subtitlesModXCenter;
+ int _subtitlesModX;
+ int _subtitlesModY;
+ int _subtitlesWidth;
+ AdRegion *_stickRegion;
+ bool _sceneIndependent;
+ bool _ignoreItems;
+ bool updateBlockRegion();
+ bool _forcedTalkAnimUsed;
+ char *_forcedTalkAnimName;
+ virtual bool getExtendedFlag(const char *flagName);
+ virtual bool resetSoundPan();
+ virtual bool updateSounds();
+ bool reset();
+ DECLARE_PERSISTENT(AdObject, BaseObject)
+ virtual void talk(const char *text, const char *sound = NULL, uint32 duration = 0, const char *stances = NULL, TTextAlign align = TAL_CENTER);
+ virtual int getHeight();
+ AdSentence *_sentence;
+ bool setFont(const char *filename);
+ virtual bool update();
+ virtual bool display();
+ bool _drawn;
+ bool _active;
+ virtual bool playAnim(const char *filename);
+ BaseSprite *_animSprite;
+ BaseSprite *_currentSprite;
+ TObjectState _state;
+ TObjectState _nextState;
+ TObjectType _type;
+ AdObject(BaseGame *inGame);
+ virtual ~AdObject();
+ BaseFont *_font;
+ BaseSprite *_tempSprite2;
+ BaseRegion *_blockRegion;
+ AdWaypointGroup *_wptGroup;
+ BaseRegion *_currentBlockRegion;
+ AdWaypointGroup *_currentWptGroup;
+ AdInventory *getInventory();
+
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ virtual bool afterMove();
+ AdRegion *_currentRegions[MAX_NUM_REGIONS];
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+
+ BaseArray<AdObject *> _attachmentsPre;
+ BaseArray<AdObject *> _attachmentsPost;
+
+ bool updateSpriteAttachments();
+ bool displaySpriteAttachments(bool preDisplay);
+ AdObject *_registerAlias;
+private:
+ bool displaySpriteAttachment(AdObject *attachment);
+ AdInventory *_inventory;
+
+protected:
+ bool getScale(float *scaleX, float *scaleY);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_path.cpp b/engines/wintermute/ad/ad_path.cpp
new file mode 100644
index 0000000000..c931213456
--- /dev/null
+++ b/engines/wintermute/ad/ad_path.cpp
@@ -0,0 +1,120 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_path.h"
+#include "engines/wintermute/base/base_point.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdPath, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdPath::AdPath(BaseGame *inGame) : BaseClass(inGame) {
+ _currIndex = -1;
+ _ready = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdPath::~AdPath() {
+ reset();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdPath::reset() {
+ for (uint32 i = 0; i < _points.size(); i++) {
+ delete _points[i];
+ }
+
+ _points.clear();
+ _currIndex = -1;
+ _ready = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BasePoint *AdPath::getFirst() {
+ if (_points.size() > 0) {
+ _currIndex = 0;
+ return _points[_currIndex];
+ } else {
+ return NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BasePoint *AdPath::getNext() {
+ _currIndex++;
+ if (_currIndex < (int32)_points.size()) {
+ return _points[_currIndex];
+ } else {
+ return NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BasePoint *AdPath::getCurrent() {
+ if (_currIndex >= 0 && _currIndex < (int32)_points.size()) {
+ return _points[_currIndex];
+ } else {
+ return NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdPath::addPoint(BasePoint *point) {
+ _points.add(point);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdPath::setReady(bool ready) {
+ bool orig = _ready;
+ _ready = ready;
+
+ return orig;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdPath::persist(BasePersistenceManager *persistMgr) {
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER(_currIndex));
+ _points.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_ready));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_path.h b/engines/wintermute/ad/ad_path.h
new file mode 100644
index 0000000000..6b043197aa
--- /dev/null
+++ b/engines/wintermute/ad/ad_path.h
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADPATH_H
+#define WINTERMUTE_ADPATH_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+class BasePoint;
+class AdPath : public BaseClass {
+public:
+ DECLARE_PERSISTENT(AdPath, BaseClass)
+ BasePoint *getCurrent();
+ bool setReady(bool ready = true);
+ void addPoint(BasePoint *point);
+ BasePoint *getNext();
+ BasePoint *getFirst();
+ void reset();
+ AdPath(BaseGame *inGame);
+ virtual ~AdPath();
+ BaseArray<BasePoint *> _points;
+ int _currIndex;
+ bool _ready;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_path_point.cpp b/engines/wintermute/ad/ad_path_point.cpp
new file mode 100644
index 0000000000..a36648eb69
--- /dev/null
+++ b/engines/wintermute/ad/ad_path_point.cpp
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_path_point.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdPathPoint, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdPathPoint::AdPathPoint() {
+ x = y = 0;
+ _distance = 0;
+
+ _marked = false;
+ _origin = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdPathPoint::AdPathPoint(int initX, int initY, int initDistance) {
+ x = initX;
+ y = initY;
+ _distance = initDistance;
+
+ _marked = false;
+ _origin = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdPathPoint::~AdPathPoint() {
+ _origin = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdPathPoint::persist(BasePersistenceManager *persistMgr) {
+
+ BasePoint::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_distance));
+ persistMgr->transfer(TMEMBER(_marked));
+ persistMgr->transfer(TMEMBER(_origin));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_path_point.h b/engines/wintermute/ad/ad_path_point.h
new file mode 100644
index 0000000000..58457976c8
--- /dev/null
+++ b/engines/wintermute/ad/ad_path_point.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADPATHPOINT_H
+#define WINTERMUTE_ADPATHPOINT_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/base_point.h"
+
+namespace Wintermute {
+
+class AdPathPoint : public BasePoint {
+public:
+ DECLARE_PERSISTENT(AdPathPoint, BasePoint)
+ AdPathPoint(int initX, int initY, int initDistance);
+ AdPathPoint();
+ virtual ~AdPathPoint();
+ AdPathPoint *_origin;
+ bool _marked;
+ int _distance;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_region.cpp b/engines/wintermute/ad/ad_region.cpp
new file mode 100644
index 0000000000..c9f1553c9a
--- /dev/null
+++ b/engines/wintermute/ad/ad_region.cpp
@@ -0,0 +1,397 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_region.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdRegion, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdRegion::AdRegion(BaseGame *inGame) : BaseRegion(inGame) {
+ _blocked = false;
+ _decoration = false;
+ _zoom = 0;
+ _alpha = 0xFFFFFFFF;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdRegion::~AdRegion() {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdRegion::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdRegion::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing REGION file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(REGION)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(NAME)
+TOKEN_DEF(ACTIVE)
+TOKEN_DEF(ZOOM)
+TOKEN_DEF(SCALE)
+TOKEN_DEF(BLOCKED)
+TOKEN_DEF(DECORATION)
+TOKEN_DEF(POINT)
+TOKEN_DEF(ALPHA_COLOR)
+TOKEN_DEF(ALPHA)
+TOKEN_DEF(EDITOR_SELECTED_POINT)
+TOKEN_DEF(EDITOR_SELECTED)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdRegion::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(REGION)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(ACTIVE)
+ TOKEN_TABLE(ZOOM)
+ TOKEN_TABLE(SCALE)
+ TOKEN_TABLE(BLOCKED)
+ TOKEN_TABLE(DECORATION)
+ TOKEN_TABLE(POINT)
+ TOKEN_TABLE(ALPHA_COLOR)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE(EDITOR_SELECTED_POINT)
+ TOKEN_TABLE(EDITOR_SELECTED)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_REGION) {
+ _gameRef->LOG(0, "'REGION' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ for (uint32 i = 0; i < _points.size(); i++) {
+ delete _points[i];
+ }
+ _points.clear();
+
+ int ar = 255, ag = 255, ab = 255, alpha = 255;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_ACTIVE:
+ parser.scanStr((char *)params, "%b", &_active);
+ break;
+
+ case TOKEN_BLOCKED:
+ parser.scanStr((char *)params, "%b", &_blocked);
+ break;
+
+ case TOKEN_DECORATION:
+ parser.scanStr((char *)params, "%b", &_decoration);
+ break;
+
+ case TOKEN_ZOOM:
+ case TOKEN_SCALE: {
+ int j;
+ parser.scanStr((char *)params, "%d", &j);
+ _zoom = (float)j;
+ }
+ break;
+
+ case TOKEN_POINT: {
+ int x, y;
+ parser.scanStr((char *)params, "%d,%d", &x, &y);
+ _points.add(new BasePoint(x, y));
+ }
+ break;
+
+ case TOKEN_ALPHA_COLOR:
+ parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab);
+ break;
+
+ case TOKEN_ALPHA:
+ parser.scanStr((char *)params, "%d", &alpha);
+ break;
+
+ case TOKEN_EDITOR_SELECTED:
+ parser.scanStr((char *)params, "%b", &_editorSelected);
+ break;
+
+ case TOKEN_EDITOR_SELECTED_POINT:
+ parser.scanStr((char *)params, "%d", &_editorSelectedPoint);
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in REGION definition");
+ return STATUS_FAILED;
+ }
+
+ createRegion();
+
+ _alpha = BYTETORGBA(ar, ag, ab, alpha);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdRegion::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // SkipTo
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SkipTo")==0) {
+ stack->correctParams(2);
+ _posX = stack->pop()->getInt();
+ _posY = stack->pop()->getInt();
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ else*/ return BaseRegion::scCallMethod(script, stack, thisStack, name);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdRegion::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("ad region");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Name") {
+ _scValue->setString(getName());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Blocked
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Blocked") {
+ _scValue->setBool(_blocked);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Decoration
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Decoration") {
+ _scValue->setBool(_decoration);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Scale
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Scale") {
+ _scValue->setFloat(_zoom);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AlphaColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AlphaColor") {
+ _scValue->setInt((int)_alpha);
+ return _scValue;
+ } else {
+ return BaseRegion::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdRegion::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Blocked
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Blocked") == 0) {
+ _blocked = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Decoration
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Decoration") == 0) {
+ _decoration = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Scale
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Scale") == 0) {
+ _zoom = value->getFloat();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AlphaColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AlphaColor") == 0) {
+ _alpha = (uint32)value->getInt();
+ return STATUS_OK;
+ } else {
+ return BaseRegion::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdRegion::scToString() {
+ return "[ad region]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdRegion::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "REGION {\n");
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+ buffer->putTextIndent(indent + 2, "BLOCKED=%s\n", _blocked ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "DECORATION=%s\n", _decoration ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "SCALE=%d\n", (int)_zoom);
+ buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alpha), RGBCOLGetG(_alpha), RGBCOLGetB(_alpha));
+ buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alpha));
+ buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE");
+
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ if (_scProp) {
+ _scProp->saveAsText(buffer, indent + 2);
+ }
+
+ for (uint32 i = 0; i < _points.size(); i++) {
+ buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y);
+ }
+
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdRegion::persist(BasePersistenceManager *persistMgr) {
+ BaseRegion::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_alpha));
+ persistMgr->transfer(TMEMBER(_blocked));
+ persistMgr->transfer(TMEMBER(_decoration));
+ persistMgr->transfer(TMEMBER(_zoom));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_region.h b/engines/wintermute/ad/ad_region.h
new file mode 100644
index 0000000000..6112900361
--- /dev/null
+++ b/engines/wintermute/ad/ad_region.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADREGION_H
+#define WINTERMUTE_ADREGION_H
+
+#include "engines/wintermute/base/base_region.h"
+
+namespace Wintermute {
+
+class AdRegion : public BaseRegion {
+public:
+ DECLARE_PERSISTENT(AdRegion, BaseRegion)
+ uint32 _alpha;
+ float _zoom;
+ bool _blocked;
+ bool _decoration;
+ AdRegion(BaseGame *inGame);
+ virtual ~AdRegion();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_response.cpp b/engines/wintermute/ad/ad_response.cpp
new file mode 100644
index 0000000000..a2225f2632
--- /dev/null
+++ b/engines/wintermute/ad/ad_response.cpp
@@ -0,0 +1,146 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_response.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/utils/utils.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdResponse, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdResponse::AdResponse(BaseGame *inGame) : BaseObject(inGame) {
+ _text = NULL;
+ _textOrig = NULL;
+ _icon = _iconHover = _iconPressed = NULL;
+ _font = NULL;
+ _iD = 0;
+ _responseType = RESPONSE_ALWAYS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdResponse::~AdResponse() {
+ delete[] _text;
+ delete[] _textOrig;
+ delete _icon;
+ delete _iconHover;
+ delete _iconPressed;
+ _text = NULL;
+ _textOrig = NULL;
+ _icon = NULL;
+ _iconHover = NULL;
+ _iconPressed = NULL;
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdResponse::setText(const char *text) {
+ BaseUtils::setString(&_text, text);
+ BaseUtils::setString(&_textOrig, text);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponse::setIcon(const char *filename) {
+ delete _icon;
+ _icon = new BaseSprite(_gameRef);
+ if (!_icon || DID_FAIL(_icon->loadFile(filename))) {
+ _gameRef->LOG(0, "AdResponse::setIcon failed for file '%s'", filename);
+ delete _icon;
+ _icon = NULL;
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponse::setFont(const char *filename) {
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ _font = _gameRef->_fontStorage->addFont(filename);
+ if (!_font) {
+ _gameRef->LOG(0, "AdResponse::setFont failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponse::setIconHover(const char *filename) {
+ delete _iconHover;
+ _iconHover = new BaseSprite(_gameRef);
+ if (!_iconHover || DID_FAIL(_iconHover->loadFile(filename))) {
+ _gameRef->LOG(0, "AdResponse::setIconHover failed for file '%s'", filename);
+ delete _iconHover;
+ _iconHover = NULL;
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponse::setIconPressed(const char *filename) {
+ delete _iconPressed;
+ _iconPressed = new BaseSprite(_gameRef);
+ if (!_iconPressed || DID_FAIL(_iconPressed->loadFile(filename))) {
+ _gameRef->LOG(0, "AdResponse::setIconPressed failed for file '%s'", filename);
+ delete _iconPressed;
+ _iconPressed = NULL;
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponse::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_icon));
+ persistMgr->transfer(TMEMBER(_iconHover));
+ persistMgr->transfer(TMEMBER(_iconPressed));
+ persistMgr->transfer(TMEMBER(_iD));
+ persistMgr->transfer(TMEMBER(_text));
+ persistMgr->transfer(TMEMBER(_textOrig));
+ persistMgr->transfer(TMEMBER_INT(_responseType));
+ persistMgr->transfer(TMEMBER(_font));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_response.h b/engines/wintermute/ad/ad_response.h
new file mode 100644
index 0000000000..0ba88cf2e8
--- /dev/null
+++ b/engines/wintermute/ad/ad_response.h
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADRESPONSE_H
+#define WINTERMUTE_ADRESPONSE_H
+
+
+#include "engines/wintermute/base/base_object.h"
+#include "engines/wintermute/ad/ad_types.h"
+
+namespace Wintermute {
+class BaseFont;
+class AdResponse : public BaseObject {
+public:
+ DECLARE_PERSISTENT(AdResponse, BaseObject)
+ bool setIcon(const char *filename);
+ bool setFont(const char *filename);
+ bool setIconHover(const char *filename);
+ bool setIconPressed(const char *filename);
+ void setText(const char *text);
+ int _iD;
+ BaseSprite *_icon;
+ BaseSprite *_iconHover;
+ BaseSprite *_iconPressed;
+ BaseFont *_font;
+ char *_text;
+ char *_textOrig;
+ AdResponse(BaseGame *inGame);
+ virtual ~AdResponse();
+ TResponseType _responseType;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_response_box.cpp b/engines/wintermute/ad/ad_response_box.cpp
new file mode 100644
index 0000000000..fb31aa0bb8
--- /dev/null
+++ b/engines/wintermute/ad/ad_response_box.cpp
@@ -0,0 +1,713 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_response.h"
+#include "engines/wintermute/ad/ad_response_box.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_surface_storage.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/ui/ui_button.h"
+#include "engines/wintermute/ui/ui_window.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/wintermute.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdResponseBox, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdResponseBox::AdResponseBox(BaseGame *inGame) : BaseObject(inGame) {
+ _font = _fontHover = NULL;
+
+ _window = NULL;
+ _shieldWindow = new UIWindow(_gameRef);
+
+ _horizontal = false;
+ BasePlatform::setRectEmpty(&_responseArea);
+ _scrollOffset = 0;
+ _spacing = 0;
+
+ _waitingScript = NULL;
+ _lastResponseText = NULL;
+ _lastResponseTextOrig = NULL;
+
+ _verticalAlign = VAL_BOTTOM;
+ _align = TAL_LEFT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdResponseBox::~AdResponseBox() {
+
+ delete _window;
+ _window = NULL;
+ delete _shieldWindow;
+ _shieldWindow = NULL;
+ delete[] _lastResponseText;
+ _lastResponseText = NULL;
+ delete[] _lastResponseTextOrig;
+ _lastResponseTextOrig = NULL;
+
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ if (_fontHover) {
+ _gameRef->_fontStorage->removeFont(_fontHover);
+ }
+
+ clearResponses();
+ clearButtons();
+
+ _waitingScript = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdResponseBox::clearResponses() {
+ for (uint32 i = 0; i < _responses.size(); i++) {
+ delete _responses[i];
+ }
+ _responses.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdResponseBox::clearButtons() {
+ for (uint32 i = 0; i < _respButtons.size(); i++) {
+ delete _respButtons[i];
+ }
+ _respButtons.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::invalidateButtons() {
+ for (uint32 i = 0; i < _respButtons.size(); i++) {
+ _respButtons[i]->_image = NULL;
+ _respButtons[i]->_cursor = NULL;
+ _respButtons[i]->_font = NULL;
+ _respButtons[i]->_fontHover = NULL;
+ _respButtons[i]->_fontPress = NULL;
+ _respButtons[i]->setText("");
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::createButtons() {
+ clearButtons();
+
+ _scrollOffset = 0;
+ for (uint32 i = 0; i < _responses.size(); i++) {
+ UIButton *btn = new UIButton(_gameRef);
+ if (btn) {
+ btn->_parent = _window;
+ btn->_sharedFonts = btn->_sharedImages = true;
+ btn->_sharedCursors = true;
+ // iconic
+ if (_responses[i]->_icon) {
+ btn->_image = _responses[i]->_icon;
+ if (_responses[i]->_iconHover) {
+ btn->_imageHover = _responses[i]->_iconHover;
+ }
+ if (_responses[i]->_iconPressed) {
+ btn->_imagePress = _responses[i]->_iconPressed;
+ }
+
+ btn->setCaption(_responses[i]->_text);
+ if (_cursor) {
+ btn->_cursor = _cursor;
+ } else if (_gameRef->_activeCursor) {
+ btn->_cursor = _gameRef->_activeCursor;
+ }
+ }
+ // textual
+ else {
+ btn->setText(_responses[i]->_text);
+ btn->_font = (_font == NULL) ? _gameRef->_systemFont : _font;
+ btn->_fontHover = (_fontHover == NULL) ? _gameRef->_systemFont : _fontHover;
+ btn->_fontPress = btn->_fontHover;
+ btn->_align = _align;
+
+ if (_gameRef->_touchInterface) {
+ btn->_fontHover = btn->_font;
+ }
+
+
+ if (_responses[i]->_font) {
+ btn->_font = _responses[i]->_font;
+ }
+
+ btn->_width = _responseArea.right - _responseArea.left;
+ if (btn->_width <= 0) {
+ btn->_width = _gameRef->_renderer->_width;
+ }
+ }
+ btn->setName("response");
+ btn->correctSize();
+
+ // make the responses touchable
+ if (_gameRef->_touchInterface) {
+ btn->_height = MAX(btn->_height, 50);
+ }
+
+ //btn->SetListener(this, btn, _responses[i]->_iD);
+ btn->setListener(this, btn, i);
+ btn->_visible = false;
+ _respButtons.add(btn);
+
+ if (_responseArea.bottom - _responseArea.top < btn->_height) {
+ _gameRef->LOG(0, "Warning: Response '%s' is too high to be displayed within response box. Correcting.", _responses[i]->_text);
+ _responseArea.bottom += (btn->_height - (_responseArea.bottom - _responseArea.top));
+ }
+ }
+ }
+ _ready = false;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdResponseBox::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing RESPONSE_BOX file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(RESPONSE_BOX)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(FONT_HOVER)
+TOKEN_DEF(FONT)
+TOKEN_DEF(AREA)
+TOKEN_DEF(HORIZONTAL)
+TOKEN_DEF(SPACING)
+TOKEN_DEF(WINDOW)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(TEXT_ALIGN)
+TOKEN_DEF(VERTICAL_ALIGN)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(RESPONSE_BOX)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(FONT_HOVER)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(AREA)
+ TOKEN_TABLE(HORIZONTAL)
+ TOKEN_TABLE(SPACING)
+ TOKEN_TABLE(WINDOW)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(TEXT_ALIGN)
+ TOKEN_TABLE(VERTICAL_ALIGN)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_RESPONSE_BOX) {
+ _gameRef->LOG(0, "'RESPONSE_BOX' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_WINDOW:
+ delete _window;
+ _window = new UIWindow(_gameRef);
+ if (!_window || DID_FAIL(_window->loadBuffer(params, false))) {
+ delete _window;
+ _window = NULL;
+ cmd = PARSERR_GENERIC;
+ } else if (_shieldWindow) {
+ _shieldWindow->_parent = _window;
+ }
+ break;
+
+ case TOKEN_FONT:
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ _font = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_font) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT_HOVER:
+ if (_fontHover) {
+ _gameRef->_fontStorage->removeFont(_fontHover);
+ }
+ _fontHover = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_fontHover) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_AREA:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_responseArea.left, &_responseArea.top, &_responseArea.right, &_responseArea.bottom);
+ break;
+
+ case TOKEN_HORIZONTAL:
+ parser.scanStr((char *)params, "%b", &_horizontal);
+ break;
+
+ case TOKEN_TEXT_ALIGN:
+ if (scumm_stricmp((char *)params, "center") == 0) {
+ _align = TAL_CENTER;
+ } else if (scumm_stricmp((char *)params, "right") == 0) {
+ _align = TAL_RIGHT;
+ } else {
+ _align = TAL_LEFT;
+ }
+ break;
+
+ case TOKEN_VERTICAL_ALIGN:
+ if (scumm_stricmp((char *)params, "top") == 0) {
+ _verticalAlign = VAL_TOP;
+ } else if (scumm_stricmp((char *)params, "center") == 0) {
+ _verticalAlign = VAL_CENTER;
+ } else {
+ _verticalAlign = VAL_BOTTOM;
+ }
+ break;
+
+ case TOKEN_SPACING:
+ parser.scanStr((char *)params, "%d", &_spacing);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in RESPONSE_BOX definition");
+ return STATUS_FAILED;
+ }
+
+ if (_window) {
+ for (uint32 i = 0; i < _window->_widgets.size(); i++) {
+ if (!_window->_widgets[i]->_listenerObject) {
+ _window->_widgets[i]->setListener(this, _window->_widgets[i], 0);
+ }
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "RESPONSE_BOX\n");
+ buffer->putTextIndent(indent, "{\n");
+
+ buffer->putTextIndent(indent + 2, "AREA { %d, %d, %d, %d }\n", _responseArea.left, _responseArea.top, _responseArea.right, _responseArea.bottom);
+
+ if (_font && _font->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename());
+ }
+ if (_fontHover && _fontHover->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT_HOVER=\"%s\"\n", _fontHover->getFilename());
+ }
+
+ if (_cursor && _cursor->getFilename()) {
+ buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename());
+ }
+
+ buffer->putTextIndent(indent + 2, "HORIZONTAL=%s\n", _horizontal ? "TRUE" : "FALSE");
+
+ switch (_align) {
+ case TAL_LEFT:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left");
+ break;
+ case TAL_RIGHT:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right");
+ break;
+ case TAL_CENTER:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center");
+ break;
+ default:
+ error("AdResponseBox::SaveAsText - Unhandled enum");
+ break;
+ }
+
+ switch (_verticalAlign) {
+ case VAL_TOP:
+ buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "top");
+ break;
+ case VAL_BOTTOM:
+ buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "bottom");
+ break;
+ case VAL_CENTER:
+ buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "center");
+ break;
+ }
+
+ buffer->putTextIndent(indent + 2, "SPACING=%d\n", _spacing);
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // window
+ if (_window) {
+ _window->saveAsText(buffer, indent + 2);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // editor properties
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::display() {
+ Rect32 rect = _responseArea;
+ if (_window) {
+ rect.offsetRect(_window->_posX, _window->_posY);
+ //_window->display();
+ }
+
+ int xxx, yyy;
+ uint32 i;
+
+ xxx = rect.left;
+ yyy = rect.top;
+
+ // shift down if needed
+ if (!_horizontal) {
+ int totalHeight = 0;
+ for (i = 0; i < _respButtons.size(); i++) {
+ totalHeight += (_respButtons[i]->_height + _spacing);
+ }
+ totalHeight -= _spacing;
+
+ switch (_verticalAlign) {
+ case VAL_BOTTOM:
+ if (yyy + totalHeight < rect.bottom) {
+ yyy = rect.bottom - totalHeight;
+ }
+ break;
+
+ case VAL_CENTER:
+ if (yyy + totalHeight < rect.bottom) {
+ yyy += ((rect.bottom - rect.top) - totalHeight) / 2;
+ }
+ break;
+
+ case VAL_TOP:
+ // do nothing
+ break;
+ }
+ }
+
+ // prepare response buttons
+ bool scrollNeeded = false;
+ for (i = _scrollOffset; i < _respButtons.size(); i++) {
+ if ((_horizontal && xxx + _respButtons[i]->_width > rect.right)
+ || (!_horizontal && yyy + _respButtons[i]->_height > rect.bottom)) {
+
+ scrollNeeded = true;
+ _respButtons[i]->_visible = false;
+ break;
+ }
+
+ _respButtons[i]->_visible = true;
+ _respButtons[i]->_posX = xxx;
+ _respButtons[i]->_posY = yyy;
+
+ if (_horizontal) {
+ xxx += (_respButtons[i]->_width + _spacing);
+ } else {
+ yyy += (_respButtons[i]->_height + _spacing);
+ }
+ }
+
+ // show appropriate scroll buttons
+ if (_window) {
+ _window->showWidget("prev", _scrollOffset > 0);
+ _window->showWidget("next", scrollNeeded);
+ }
+
+ // go exclusive
+ if (_shieldWindow) {
+ _shieldWindow->_posX = _shieldWindow->_posY = 0;
+ _shieldWindow->_width = _gameRef->_renderer->_width;
+ _shieldWindow->_height = _gameRef->_renderer->_height;
+
+ _shieldWindow->display();
+ }
+
+ // display window
+ if (_window) {
+ _window->display();
+ }
+
+
+ // display response buttons
+ for (i = _scrollOffset; i < _respButtons.size(); i++) {
+ _respButtons[i]->display();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::listen(BaseScriptHolder *param1, uint32 param2) {
+ UIObject *obj = (UIObject *)param1;
+
+ switch (obj->_type) {
+ case UI_BUTTON:
+ if (scumm_stricmp(obj->getName(), "prev") == 0) {
+ _scrollOffset--;
+ } else if (scumm_stricmp(obj->getName(), "next") == 0) {
+ _scrollOffset++;
+ } else if (scumm_stricmp(obj->getName(), "response") == 0) {
+ if (_waitingScript) {
+ _waitingScript->_stack->pushInt(_responses[param2]->_iD);
+ }
+ handleResponse(_responses[param2]);
+ _waitingScript = NULL;
+ _gameRef->_state = GAME_RUNNING;
+ ((AdGame *)_gameRef)->_stateEx = GAME_NORMAL;
+ _ready = true;
+ invalidateButtons();
+ clearResponses();
+ } else {
+ return BaseObject::listen(param1, param2);
+ }
+ break;
+ default:
+ error("AdResponseBox::Listen - Unhandled enum");
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::persist(BasePersistenceManager *persistMgr) {
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_font));
+ persistMgr->transfer(TMEMBER(_fontHover));
+ persistMgr->transfer(TMEMBER(_horizontal));
+ persistMgr->transfer(TMEMBER(_lastResponseText));
+ persistMgr->transfer(TMEMBER(_lastResponseTextOrig));
+ _respButtons.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_responseArea));
+ _responses.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_scrollOffset));
+ persistMgr->transfer(TMEMBER(_shieldWindow));
+ persistMgr->transfer(TMEMBER(_spacing));
+ persistMgr->transfer(TMEMBER(_waitingScript));
+ persistMgr->transfer(TMEMBER(_window));
+
+ persistMgr->transfer(TMEMBER_INT(_verticalAlign));
+ persistMgr->transfer(TMEMBER_INT(_align));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::weedResponses() {
+ AdGame *adGame = (AdGame *)_gameRef;
+
+ for (uint32 i = 0; i < _responses.size(); i++) {
+ switch (_responses[i]->_responseType) {
+ case RESPONSE_ONCE:
+ if (adGame->branchResponseUsed(_responses[i]->_iD)) {
+ delete _responses[i];
+ _responses.remove_at(i);
+ i--;
+ }
+ break;
+
+ case RESPONSE_ONCE_GAME:
+ if (adGame->gameResponseUsed(_responses[i]->_iD)) {
+ delete _responses[i];
+ _responses.remove_at(i);
+ i--;
+ }
+ break;
+ default:
+ debugC(kWintermuteDebugGeneral, "AdResponseBox::WeedResponses - Unhandled enum");
+ break;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdResponseBox::setLastResponseText(const char *text, const char *textOrig) {
+ BaseUtils::setString(&_lastResponseText, text);
+ BaseUtils::setString(&_lastResponseTextOrig, textOrig);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::handleResponse(AdResponse *response) {
+ setLastResponseText(response->_text, response->_textOrig);
+
+ AdGame *adGame = (AdGame *)_gameRef;
+
+ switch (response->_responseType) {
+ case RESPONSE_ONCE:
+ adGame->addBranchResponse(response->_iD);
+ break;
+
+ case RESPONSE_ONCE_GAME:
+ adGame->addGameResponse(response->_iD);
+ break;
+ default:
+ debugC(kWintermuteDebugGeneral, "AdResponseBox::HandleResponse - Unhandled enum");
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseObject *AdResponseBox::getNextAccessObject(BaseObject *currObject) {
+ BaseArray<UIObject *> objects;
+ getObjects(objects, true);
+
+ if (objects.size() == 0) {
+ return NULL;
+ } else {
+ if (currObject != NULL) {
+ for (uint32 i = 0; i < objects.size(); i++) {
+ if (objects[i] == currObject) {
+ if (i < objects.size() - 1) {
+ return objects[i + 1];
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ return objects[0];
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseObject *AdResponseBox::getPrevAccessObject(BaseObject *currObject) {
+ BaseArray<UIObject *> objects;
+ getObjects(objects, true);
+
+ if (objects.size() == 0) {
+ return NULL;
+ } else {
+ if (currObject != NULL) {
+ for (int i = objects.size() - 1; i >= 0; i--) {
+ if (objects[i] == currObject) {
+ if (i > 0) {
+ return objects[i - 1];
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ return objects[objects.size() - 1];
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseBox::getObjects(BaseArray<UIObject *> &objects, bool interactiveOnly) {
+ for (uint32 i = 0; i < _respButtons.size(); i++) {
+ objects.add(_respButtons[i]);
+ }
+ if (_window) {
+ _window->getWindowObjects(objects, interactiveOnly);
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_response_box.h b/engines/wintermute/ad/ad_response_box.h
new file mode 100644
index 0000000000..35f8cb6347
--- /dev/null
+++ b/engines/wintermute/ad/ad_response_box.h
@@ -0,0 +1,87 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADRESPONSEBOX_H
+#define WINTERMUTE_ADRESPONSEBOX_H
+
+
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+
+class UIButton;
+class UIWindow;
+class UIObject;
+class AdResponse;
+class AdResponseBox : public BaseObject {
+public:
+ BaseObject *getNextAccessObject(BaseObject *CurrObject);
+ BaseObject *getPrevAccessObject(BaseObject *CurrObject);
+ bool getObjects(BaseArray<UIObject *> &objects, bool interactiveOnly);
+
+ bool handleResponse(AdResponse *response);
+ void setLastResponseText(const char *text, const char *textOrig);
+ char *_lastResponseText;
+ char *_lastResponseTextOrig;
+ DECLARE_PERSISTENT(AdResponseBox, BaseObject)
+ ScScript *_waitingScript;
+ virtual bool listen(BaseScriptHolder *param1, uint32 param2);
+ typedef enum {
+ EVENT_PREV,
+ EVENT_NEXT,
+ EVENT_RESPONSE
+ } TResponseEvent;
+
+ bool weedResponses();
+ bool display();
+ int _spacing;
+ int _scrollOffset;
+ BaseFont *_fontHover;
+ BaseFont *_font;
+ bool createButtons();
+ bool invalidateButtons();
+ void clearButtons();
+ void clearResponses();
+ AdResponseBox(BaseGame *inGame);
+ virtual ~AdResponseBox();
+ BaseArray<AdResponse *> _responses;
+ BaseArray<UIButton *> _respButtons;
+ UIWindow *_window;
+ UIWindow *_shieldWindow;
+ bool _horizontal;
+ Rect32 _responseArea;
+ int _verticalAlign;
+ TTextAlign _align;
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_response_context.cpp b/engines/wintermute/ad/ad_response_context.cpp
new file mode 100644
index 0000000000..ebfa03feea
--- /dev/null
+++ b/engines/wintermute/ad/ad_response_context.cpp
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_response_context.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdResponseContext, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdResponseContext::AdResponseContext(BaseGame *inGame) : BaseClass(inGame) {
+ _id = 0;
+ _context = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdResponseContext::~AdResponseContext() {
+ delete[] _context;
+ _context = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdResponseContext::persist(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_gameRef));
+ persistMgr->transfer(TMEMBER(_context));
+ persistMgr->transfer(TMEMBER(_id));
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void AdResponseContext::setContext(const char *context) {
+ delete[] _context;
+ _context = NULL;
+ if (context) {
+ _context = new char [strlen(context) + 1];
+ if (_context) {
+ strcpy(_context, context);
+ }
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_response_context.h b/engines/wintermute/ad/ad_response_context.h
new file mode 100644
index 0000000000..14bc1abd93
--- /dev/null
+++ b/engines/wintermute/ad/ad_response_context.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADRESPONSECONTEXT_H
+#define WINTERMUTE_ADRESPONSECONTEXT_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+class AdResponseContext : public BaseClass {
+public:
+ void setContext(const char *context);
+ int _id;
+ char *_context;
+ DECLARE_PERSISTENT(AdResponseContext, BaseClass)
+ AdResponseContext(BaseGame *inGame);
+ virtual ~AdResponseContext();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_rot_level.cpp b/engines/wintermute/ad/ad_rot_level.cpp
new file mode 100644
index 0000000000..fb9a4a47b9
--- /dev/null
+++ b/engines/wintermute/ad/ad_rot_level.cpp
@@ -0,0 +1,161 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_rot_level.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_sprite.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdRotLevel, false)
+
+
+//////////////////////////////////////////////////////////////////////////
+AdRotLevel::AdRotLevel(BaseGame *inGame) : BaseObject(inGame) {
+ _posX = 0;
+ _rotation = 0.0f;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdRotLevel::~AdRotLevel() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdRotLevel::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdRotLevel::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing ROTATION_LEVEL file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(ROTATION_LEVEL)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(X)
+TOKEN_DEF(ROTATION)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdRotLevel::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ROTATION_LEVEL)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(X)
+ TOKEN_TABLE(ROTATION)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_ROTATION_LEVEL) {
+ _gameRef->LOG(0, "'ROTATION_LEVEL' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_X:
+ parser.scanStr((char *)params, "%d", &_posX);
+ break;
+
+ case TOKEN_ROTATION: {
+ int i;
+ parser.scanStr((char *)params, "%d", &i);
+ _rotation = (float)i;
+ }
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in ROTATION_LEVEL definition");
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdRotLevel::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "ROTATION_LEVEL {\n");
+ buffer->putTextIndent(indent + 2, "X=%d\n", _posX);
+ buffer->putTextIndent(indent + 2, "ROTATION=%d\n", (int)_rotation);
+ BaseClass::saveAsText(buffer, indent + 2);
+ buffer->putTextIndent(indent, "}\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdRotLevel::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_rotation));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_rot_level.h b/engines/wintermute/ad/ad_rot_level.h
new file mode 100644
index 0000000000..d7f5f8edf0
--- /dev/null
+++ b/engines/wintermute/ad/ad_rot_level.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADROTLEVEL_H
+#define WINTERMUTE_ADROTLEVEL_H
+
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+
+class AdRotLevel : public BaseObject {
+public:
+ DECLARE_PERSISTENT(AdRotLevel, BaseObject)
+ AdRotLevel(BaseGame *inGame);
+ virtual ~AdRotLevel();
+ float _rotation;
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_scale_level.cpp b/engines/wintermute/ad/ad_scale_level.cpp
new file mode 100644
index 0000000000..4e9293d875
--- /dev/null
+++ b/engines/wintermute/ad/ad_scale_level.cpp
@@ -0,0 +1,159 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_scale_level.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdScaleLevel, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdScaleLevel::AdScaleLevel(BaseGame *inGame) : BaseObject(inGame) {
+ _posY = 0;
+ _scale = 100;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdScaleLevel::~AdScaleLevel() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScaleLevel::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdScaleLevel::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing SCALE_LEVEL file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(SCALE_LEVEL)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(Y)
+TOKEN_DEF(SCALE)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdScaleLevel::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(SCALE_LEVEL)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(Y)
+ TOKEN_TABLE(SCALE)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_SCALE_LEVEL) {
+ _gameRef->LOG(0, "'SCALE_LEVEL' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_Y:
+ parser.scanStr((char *)params, "%d", &_posY);
+ break;
+
+ case TOKEN_SCALE: {
+ int i;
+ parser.scanStr((char *)params, "%d", &i);
+ _scale = (float)i;
+ }
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in SCALE_LEVEL definition");
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScaleLevel::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "SCALE_LEVEL {\n");
+ buffer->putTextIndent(indent + 2, "Y=%d\n", _posY);
+ buffer->putTextIndent(indent + 2, "SCALE=%d\n", (int)_scale);
+ BaseClass::saveAsText(buffer, indent + 2);
+ buffer->putTextIndent(indent, "}\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScaleLevel::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_scale));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_scale_level.h b/engines/wintermute/ad/ad_scale_level.h
new file mode 100644
index 0000000000..628a385eb4
--- /dev/null
+++ b/engines/wintermute/ad/ad_scale_level.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADSCALELEVEL_H
+#define WINTERMUTE_ADSCALELEVEL_H
+
+
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+
+class AdScaleLevel : public BaseObject {
+public:
+ DECLARE_PERSISTENT(AdScaleLevel, BaseObject)
+ float _scale;
+ AdScaleLevel(BaseGame *inGame);
+ virtual ~AdScaleLevel();
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_scene.cpp b/engines/wintermute/ad/ad_scene.cpp
new file mode 100644
index 0000000000..8e9beca0c0
--- /dev/null
+++ b/engines/wintermute/ad/ad_scene.cpp
@@ -0,0 +1,2987 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/ad/ad_actor.h"
+#include "engines/wintermute/ad/ad_entity.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_layer.h"
+#include "engines/wintermute/ad/ad_node_state.h"
+#include "engines/wintermute/ad/ad_object.h"
+#include "engines/wintermute/ad/ad_path.h"
+#include "engines/wintermute/ad/ad_path_point.h"
+#include "engines/wintermute/ad/ad_rot_level.h"
+#include "engines/wintermute/ad/ad_scale_level.h"
+#include "engines/wintermute/ad/ad_scene_node.h"
+#include "engines/wintermute/ad/ad_scene_state.h"
+#include "engines/wintermute/ad/ad_sentence.h"
+#include "engines/wintermute/ad/ad_waypoint_group.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_object.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_point.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/base_scriptable.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_viewport.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/ui/ui_window.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/wintermute.h"
+#include <limits.h>
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdScene, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdScene::AdScene(BaseGame *inGame) : BaseObject(inGame) {
+ _pfTarget = new BasePoint;
+ setDefaults();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdScene::~AdScene() {
+ cleanup();
+ _gameRef->unregisterObject(_fader);
+ delete _pfTarget;
+ _pfTarget = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::setDefaults() {
+ _initialized = false;
+ _pfReady = true;
+ _pfTargetPath = NULL;
+ _pfRequester = NULL;
+ _mainLayer = NULL;
+
+ _pfPointsNum = 0;
+ _persistentState = false;
+ _persistentStateSprites = true;
+
+ _autoScroll = true;
+ _offsetLeft = _offsetTop = 0;
+ _targetOffsetLeft = _targetOffsetTop = 0;
+
+ _lastTimeH = _lastTimeV = 0;
+ _scrollTimeH = _scrollTimeV = 10;
+ _scrollPixelsH = _scrollPixelsV = 1;
+
+ _pfMaxTime = 15;
+
+ _paralaxScrolling = true;
+
+ // editor settings
+ _editorMarginH = _editorMarginV = 100;
+
+ _editorColFrame = 0xE0888888;
+ _editorColEntity = 0xFF008000;
+ _editorColRegion = 0xFF0000FF;
+ _editorColBlocked = 0xFF800080;
+ _editorColWaypoints = 0xFF0000FF;
+ _editorColEntitySel = 0xFFFF0000;
+ _editorColRegionSel = 0xFFFF0000;
+ _editorColBlockedSel = 0xFFFF0000;
+ _editorColWaypointsSel = 0xFFFF0000;
+ _editorColScale = 0xFF00FF00;
+ _editorColDecor = 0xFF00FFFF;
+ _editorColDecorSel = 0xFFFF0000;
+
+ _editorShowRegions = true;
+ _editorShowBlocked = true;
+ _editorShowDecor = true;
+ _editorShowEntities = true;
+ _editorShowScale = true;
+
+ _shieldWindow = NULL;
+
+ _fader = new BaseFader(_gameRef);
+ _gameRef->registerObject(_fader);
+
+ _viewport = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::cleanup() {
+ BaseObject::cleanup();
+
+ _mainLayer = NULL; // reference only
+
+ delete _shieldWindow;
+ _shieldWindow = NULL;
+
+ _gameRef->unregisterObject(_fader);
+ _fader = NULL;
+
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ _gameRef->unregisterObject(_layers[i]);
+ }
+ _layers.clear();
+
+
+ for (uint32 i = 0; i < _waypointGroups.size(); i++) {
+ _gameRef->unregisterObject(_waypointGroups[i]);
+ }
+ _waypointGroups.clear();
+
+ for (uint32 i = 0; i < _scaleLevels.size(); i++) {
+ _gameRef->unregisterObject(_scaleLevels[i]);
+ }
+ _scaleLevels.clear();
+
+ for (uint32 i = 0; i < _rotLevels.size(); i++) {
+ _gameRef->unregisterObject(_rotLevels[i]);
+ }
+ _rotLevels.clear();
+
+
+ for (uint32 i = 0; i < _pfPath.size(); i++) {
+ delete _pfPath[i];
+ }
+ _pfPath.clear();
+ _pfPointsNum = 0;
+
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ _gameRef->unregisterObject(_objects[i]);
+ }
+ _objects.clear();
+
+ delete _viewport;
+ _viewport = NULL;
+
+ setDefaults();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::getPath(BasePoint source, BasePoint target, AdPath *path, BaseObject *requester) {
+ if (!_pfReady) {
+ return false;
+ } else {
+ _pfReady = false;
+ *_pfTarget = target;
+ _pfTargetPath = path;
+ _pfRequester = requester;
+
+ _pfTargetPath->reset();
+ _pfTargetPath->setReady(false);
+
+ // prepare working path
+ pfPointsStart();
+
+ // first point
+ //_pfPath.add(new AdPathPoint(source.x, source.y, 0));
+
+ // if we're one pixel stuck, get unstuck
+ int startX = source.x;
+ int startY = source.y;
+ int bestDistance = 1000;
+ if (isBlockedAt(startX, startY, true, requester)) {
+ int tolerance = 2;
+ for (int xxx = startX - tolerance; xxx <= startX + tolerance; xxx++) {
+ for (int yyy = startY - tolerance; yyy <= startY + tolerance; yyy++) {
+ if (isWalkableAt(xxx, yyy, true, requester)) {
+ int distance = abs(xxx - source.x) + abs(yyy - source.y);
+ if (distance < bestDistance) {
+ startX = xxx;
+ startY = yyy;
+
+ bestDistance = distance;
+ }
+ }
+ }
+ }
+ }
+
+ pfPointsAdd(startX, startY, 0);
+
+ //CorrectTargetPoint(&target.x, &target.y);
+
+ // last point
+ //_pfPath.add(new AdPathPoint(target.x, target.y, INT_MAX));
+ pfPointsAdd(target.x, target.y, INT_MAX);
+
+ // active waypoints
+ for (uint32 i = 0; i < _waypointGroups.size(); i++) {
+ if (_waypointGroups[i]->_active) {
+ pfAddWaypointGroup(_waypointGroups[i], requester);
+ }
+ }
+
+
+ // free waypoints
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentWptGroup) {
+ pfAddWaypointGroup(_objects[i]->_currentWptGroup, requester);
+ }
+ }
+ AdGame *adGame = (AdGame *)_gameRef;
+ for (uint32 i = 0; i < adGame->_objects.size(); i++) {
+ if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentWptGroup) {
+ pfAddWaypointGroup(adGame->_objects[i]->_currentWptGroup, requester);
+ }
+ }
+
+ return true;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::pfAddWaypointGroup(AdWaypointGroup *wpt, BaseObject *requester) {
+ if (!wpt->_active) {
+ return;
+ }
+
+ for (uint32 i = 0; i < wpt->_points.size(); i++) {
+ if (isBlockedAt(wpt->_points[i]->x, wpt->_points[i]->y, true, requester)) {
+ continue;
+ }
+
+ //_pfPath.add(new AdPathPoint(Wpt->_points[i]->x, Wpt->_points[i]->y, INT_MAX));
+ pfPointsAdd(wpt->_points[i]->x, wpt->_points[i]->y, INT_MAX);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float AdScene::getZoomAt(int x, int y) {
+ float ret = 100;
+
+ bool found = false;
+ if (_mainLayer) {
+ for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) {
+ AdSceneNode *node = _mainLayer->_nodes[i];
+ if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->_blocked && node->_region->pointInRegion(x, y)) {
+ if (node->_region->_zoom != 0) {
+ ret = node->_region->_zoom;
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!found) {
+ ret = getScaleAt(y);
+ }
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 AdScene::getAlphaAt(int x, int y, bool colorCheck) {
+ if (!_gameRef->_debugDebugMode) {
+ colorCheck = false;
+ }
+
+ uint32 ret;
+ if (colorCheck) {
+ ret = 0xFFFF0000;
+ } else {
+ ret = 0xFFFFFFFF;
+ }
+
+ if (_mainLayer) {
+ for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) {
+ AdSceneNode *node = _mainLayer->_nodes[i];
+ if (node->_type == OBJECT_REGION && node->_region->_active && (colorCheck || !node->_region->_blocked) && node->_region->pointInRegion(x, y)) {
+ if (!node->_region->_blocked) {
+ ret = node->_region->_alpha;
+ }
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::isBlockedAt(int x, int y, bool checkFreeObjects, BaseObject *requester) {
+ bool ret = true;
+
+ if (checkFreeObjects) {
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentBlockRegion) {
+ if (_objects[i]->_currentBlockRegion->pointInRegion(x, y)) {
+ return true;
+ }
+ }
+ }
+ AdGame *adGame = (AdGame *)_gameRef;
+ for (uint32 i = 0; i < adGame->_objects.size(); i++) {
+ if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentBlockRegion) {
+ if (adGame->_objects[i]->_currentBlockRegion->pointInRegion(x, y)) {
+ return true;
+ }
+ }
+ }
+ }
+
+
+ if (_mainLayer) {
+ for (uint32 i = 0; i < _mainLayer->_nodes.size(); i++) {
+ AdSceneNode *node = _mainLayer->_nodes[i];
+ /*
+ if (Node->_type == OBJECT_REGION && Node->_region->_active && Node->_region->_blocked && Node->_region->PointInRegion(X, Y))
+ {
+ ret = true;
+ break;
+ }
+ */
+ if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->_decoration && node->_region->pointInRegion(x, y)) {
+ if (node->_region->_blocked) {
+ ret = true;
+ break;
+ } else {
+ ret = false;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::isWalkableAt(int x, int y, bool checkFreeObjects, BaseObject *requester) {
+ bool ret = false;
+
+ if (checkFreeObjects) {
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentBlockRegion) {
+ if (_objects[i]->_currentBlockRegion->pointInRegion(x, y)) {
+ return false;
+ }
+ }
+ }
+ AdGame *adGame = (AdGame *)_gameRef;
+ for (uint32 i = 0; i < adGame->_objects.size(); i++) {
+ if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentBlockRegion) {
+ if (adGame->_objects[i]->_currentBlockRegion->pointInRegion(x, y)) {
+ return false;
+ }
+ }
+ }
+ }
+
+
+ if (_mainLayer) {
+ for (uint32 i = 0; i < _mainLayer->_nodes.size(); i++) {
+ AdSceneNode *node = _mainLayer->_nodes[i];
+ if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->_decoration && node->_region->pointInRegion(x, y)) {
+ if (node->_region->_blocked) {
+ ret = false;
+ break;
+ } else {
+ ret = true;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int AdScene::getPointsDist(BasePoint p1, BasePoint p2, BaseObject *requester) {
+ double xStep, yStep, x, y;
+ int xLength, yLength, xCount, yCount;
+ int x1, y1, x2, y2;
+
+ x1 = p1.x;
+ y1 = p1.y;
+ x2 = p2.x;
+ y2 = p2.y;
+
+ xLength = abs(x2 - x1);
+ yLength = abs(y2 - y1);
+
+ if (xLength > yLength) {
+ if (x1 > x2) {
+ BaseUtils::swap(&x1, &x2);
+ BaseUtils::swap(&y1, &y2);
+ }
+
+ yStep = (double)(y2 - y1) / (double)(x2 - x1);
+ y = y1;
+
+ for (xCount = x1; xCount < x2; xCount++) {
+ if (isBlockedAt(xCount, (int)y, true, requester)) {
+ return -1;
+ }
+ y += yStep;
+ }
+ } else {
+ if (y1 > y2) {
+ BaseUtils::swap(&x1, &x2);
+ BaseUtils::swap(&y1, &y2);
+ }
+
+ xStep = (double)(x2 - x1) / (double)(y2 - y1);
+ x = x1;
+
+ for (yCount = y1; yCount < y2; yCount++) {
+ if (isBlockedAt((int)x, yCount, true, requester)) {
+ return -1;
+ }
+ x += xStep;
+ }
+ }
+ return MAX(xLength, yLength);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::pathFinderStep() {
+ int i;
+ // get lowest unmarked
+ int lowestDist = INT_MAX;
+ AdPathPoint *lowestPt = NULL;
+
+ for (i = 0; i < _pfPointsNum; i++)
+ if (!_pfPath[i]->_marked && _pfPath[i]->_distance < lowestDist) {
+ lowestDist = _pfPath[i]->_distance;
+ lowestPt = _pfPath[i];
+ }
+
+ if (lowestPt == NULL) { // no path -> terminate PathFinder
+ _pfReady = true;
+ _pfTargetPath->setReady(true);
+ return;
+ }
+
+ lowestPt->_marked = true;
+
+ // target point marked, generate path and terminate
+ if (lowestPt->x == _pfTarget->x && lowestPt->y == _pfTarget->y) {
+ while (lowestPt != NULL) {
+ _pfTargetPath->_points.insert_at(0, new BasePoint(lowestPt->x, lowestPt->y));
+ lowestPt = lowestPt->_origin;
+ }
+
+ _pfReady = true;
+ _pfTargetPath->setReady(true);
+ return;
+ }
+
+ // otherwise keep on searching
+ for (i = 0; i < _pfPointsNum; i++)
+ if (!_pfPath[i]->_marked) {
+ int j = getPointsDist(*lowestPt, *_pfPath[i], _pfRequester);
+ if (j != -1 && lowestPt->_distance + j < _pfPath[i]->_distance) {
+ _pfPath[i]->_distance = lowestPt->_distance + j;
+ _pfPath[i]->_origin = lowestPt;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::initLoop() {
+#ifdef _DEBUGxxxx
+ int nu_steps = 0;
+ uint32 start = _gameRef->_currentTime;
+ while (!_pfReady && g_system->getMillis() - start <= _pfMaxTime) {
+ PathFinderStep();
+ nu_steps++;
+ }
+ if (nu_steps > 0) {
+ _gameRef->LOG(0, "STAT: PathFinder iterations in one loop: %d (%s) _pfMaxTime=%d", nu_steps, _pfReady ? "finished" : "not yet done", _pfMaxTime);
+ }
+#else
+ uint32 start = _gameRef->_currentTime;
+ while (!_pfReady && g_system->getMillis() - start <= _pfMaxTime) {
+ pathFinderStep();
+ }
+#endif
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdScene::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing SCENE file '%s'", filename);
+ }
+
+ setFilename(filename);
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(SCENE)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(NAME)
+TOKEN_DEF(LAYER)
+TOKEN_DEF(WAYPOINTS)
+TOKEN_DEF(EVENTS)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(CAMERA)
+TOKEN_DEF(ENTITY)
+TOKEN_DEF(SCALE_LEVEL)
+TOKEN_DEF(ROTATION_LEVEL)
+TOKEN_DEF(EDITOR_MARGIN_H)
+TOKEN_DEF(EDITOR_MARGIN_V)
+TOKEN_DEF(EDITOR_COLOR_FRAME)
+TOKEN_DEF(EDITOR_COLOR_ENTITY_SEL)
+TOKEN_DEF(EDITOR_COLOR_REGION_SEL)
+TOKEN_DEF(EDITOR_COLOR_DECORATION_SEL)
+TOKEN_DEF(EDITOR_COLOR_BLOCKED_SEL)
+TOKEN_DEF(EDITOR_COLOR_WAYPOINTS_SEL)
+TOKEN_DEF(EDITOR_COLOR_REGION)
+TOKEN_DEF(EDITOR_COLOR_DECORATION)
+TOKEN_DEF(EDITOR_COLOR_BLOCKED)
+TOKEN_DEF(EDITOR_COLOR_ENTITY)
+TOKEN_DEF(EDITOR_COLOR_WAYPOINTS)
+TOKEN_DEF(EDITOR_COLOR_SCALE)
+TOKEN_DEF(EDITOR_SHOW_REGIONS)
+TOKEN_DEF(EDITOR_SHOW_BLOCKED)
+TOKEN_DEF(EDITOR_SHOW_DECORATION)
+TOKEN_DEF(EDITOR_SHOW_ENTITIES)
+TOKEN_DEF(EDITOR_SHOW_SCALE)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(VIEWPORT)
+TOKEN_DEF(PERSISTENT_STATE_SPRITES)
+TOKEN_DEF(PERSISTENT_STATE)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(SCENE)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(LAYER)
+ TOKEN_TABLE(WAYPOINTS)
+ TOKEN_TABLE(EVENTS)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(CAMERA)
+ TOKEN_TABLE(ENTITY)
+ TOKEN_TABLE(SCALE_LEVEL)
+ TOKEN_TABLE(ROTATION_LEVEL)
+ TOKEN_TABLE(EDITOR_MARGIN_H)
+ TOKEN_TABLE(EDITOR_MARGIN_V)
+ TOKEN_TABLE(EDITOR_COLOR_FRAME)
+ TOKEN_TABLE(EDITOR_COLOR_ENTITY_SEL)
+ TOKEN_TABLE(EDITOR_COLOR_REGION_SEL)
+ TOKEN_TABLE(EDITOR_COLOR_DECORATION_SEL)
+ TOKEN_TABLE(EDITOR_COLOR_BLOCKED_SEL)
+ TOKEN_TABLE(EDITOR_COLOR_WAYPOINTS_SEL)
+ TOKEN_TABLE(EDITOR_COLOR_REGION)
+ TOKEN_TABLE(EDITOR_COLOR_DECORATION)
+ TOKEN_TABLE(EDITOR_COLOR_BLOCKED)
+ TOKEN_TABLE(EDITOR_COLOR_ENTITY)
+ TOKEN_TABLE(EDITOR_COLOR_WAYPOINTS)
+ TOKEN_TABLE(EDITOR_COLOR_SCALE)
+ TOKEN_TABLE(EDITOR_SHOW_REGIONS)
+ TOKEN_TABLE(EDITOR_SHOW_DECORATION)
+ TOKEN_TABLE(EDITOR_SHOW_BLOCKED)
+ TOKEN_TABLE(EDITOR_SHOW_ENTITIES)
+ TOKEN_TABLE(EDITOR_SHOW_SCALE)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(VIEWPORT)
+ TOKEN_TABLE(PERSISTENT_STATE_SPRITES)
+ TOKEN_TABLE(PERSISTENT_STATE)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ cleanup();
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_SCENE) {
+ _gameRef->LOG(0, "'SCENE' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ int ar, ag, ab, aa;
+ char camera[MAX_PATH_LENGTH] = "";
+ /* float waypointHeight = -1.0f; */
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_LAYER: {
+ AdLayer *layer = new AdLayer(_gameRef);
+ if (!layer || DID_FAIL(layer->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ delete layer;
+ layer = NULL;
+ } else {
+ _gameRef->registerObject(layer);
+ _layers.add(layer);
+ if (layer->_main) {
+ _mainLayer = layer;
+ _width = layer->_width;
+ _height = layer->_height;
+ }
+ }
+ }
+ break;
+
+ case TOKEN_WAYPOINTS: {
+ AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef);
+ if (!wpt || DID_FAIL(wpt->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ delete wpt;
+ wpt = NULL;
+ } else {
+ _gameRef->registerObject(wpt);
+ _waypointGroups.add(wpt);
+ }
+ }
+ break;
+
+ case TOKEN_SCALE_LEVEL: {
+ AdScaleLevel *sl = new AdScaleLevel(_gameRef);
+ if (!sl || DID_FAIL(sl->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ delete sl;
+ sl = NULL;
+ } else {
+ _gameRef->registerObject(sl);
+ _scaleLevels.add(sl);
+ }
+ }
+ break;
+
+ case TOKEN_ROTATION_LEVEL: {
+ AdRotLevel *rl = new AdRotLevel(_gameRef);
+ if (!rl || DID_FAIL(rl->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ delete rl;
+ rl = NULL;
+ } else {
+ _gameRef->registerObject(rl);
+ _rotLevels.add(rl);
+ }
+ }
+ break;
+
+ case TOKEN_ENTITY: {
+ AdEntity *entity = new AdEntity(_gameRef);
+ if (!entity || DID_FAIL(entity->loadBuffer(params, false))) {
+ cmd = PARSERR_GENERIC;
+ delete entity;
+ entity = NULL;
+ } else {
+ addObject(entity);
+ }
+ }
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_CAMERA:
+ strcpy(camera, (char *)params);
+ break;
+
+ case TOKEN_EDITOR_MARGIN_H:
+ parser.scanStr((char *)params, "%d", &_editorMarginH);
+ break;
+
+ case TOKEN_EDITOR_MARGIN_V:
+ parser.scanStr((char *)params, "%d", &_editorMarginV);
+ break;
+
+ case TOKEN_EDITOR_COLOR_FRAME:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColFrame = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_ENTITY:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColEntity = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_ENTITY_SEL:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColEntitySel = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_REGION_SEL:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColRegionSel = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_DECORATION_SEL:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColDecorSel = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_BLOCKED_SEL:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColBlockedSel = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_WAYPOINTS_SEL:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColWaypointsSel = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_REGION:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColRegion = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_DECORATION:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColDecor = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_BLOCKED:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColBlocked = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_WAYPOINTS:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColWaypoints = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_COLOR_SCALE:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa);
+ _editorColScale = BYTETORGBA(ar, ag, ab, aa);
+ break;
+
+ case TOKEN_EDITOR_SHOW_REGIONS:
+ parser.scanStr((char *)params, "%b", &_editorShowRegions);
+ break;
+
+ case TOKEN_EDITOR_SHOW_BLOCKED:
+ parser.scanStr((char *)params, "%b", &_editorShowBlocked);
+ break;
+
+ case TOKEN_EDITOR_SHOW_DECORATION:
+ parser.scanStr((char *)params, "%b", &_editorShowDecor);
+ break;
+
+ case TOKEN_EDITOR_SHOW_ENTITIES:
+ parser.scanStr((char *)params, "%b", &_editorShowEntities);
+ break;
+
+ case TOKEN_EDITOR_SHOW_SCALE:
+ parser.scanStr((char *)params, "%b", &_editorShowScale);
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+
+ case TOKEN_VIEWPORT: {
+ Rect32 rc;
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &rc.left, &rc.top, &rc.right, &rc.bottom);
+ if (!_viewport) {
+ _viewport = new BaseViewport(_gameRef);
+ }
+ if (_viewport) {
+ _viewport->setRect(rc.left, rc.top, rc.right, rc.bottom, true);
+ }
+ }
+
+ case TOKEN_PERSISTENT_STATE:
+ parser.scanStr((char *)params, "%b", &_persistentState);
+ break;
+
+ case TOKEN_PERSISTENT_STATE_SPRITES:
+ parser.scanStr((char *)params, "%b", &_persistentStateSprites);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in SCENE definition");
+ return STATUS_FAILED;
+ }
+
+ if (_mainLayer == NULL) {
+ _gameRef->LOG(0, "Warning: scene '%s' has no main layer.", getFilename());
+ }
+
+
+ sortScaleLevels();
+ sortRotLevels();
+
+ _initialized = true;
+
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::traverseNodes(bool doUpdate) {
+ if (!_initialized) {
+ return STATUS_OK;
+ }
+
+ AdGame *adGame = (AdGame *)_gameRef;
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // prepare viewport
+ bool popViewport = false;
+ if (_viewport && !_gameRef->_editorMode) {
+ _gameRef->pushViewport(_viewport);
+ popViewport = true;
+ } else if (adGame->_sceneViewport && !_gameRef->_editorMode) {
+ _gameRef->pushViewport(adGame->_sceneViewport);
+ popViewport = true;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // *** adjust scroll offset
+ if (doUpdate) {
+ /*
+ if (_autoScroll && _gameRef->_mainObject != NULL)
+ {
+ ScrollToObject(_gameRef->_mainObject);
+ }
+ */
+
+ if (_autoScroll) {
+ // adjust horizontal scroll
+ if (_gameRef->_timer - _lastTimeH >= _scrollTimeH) {
+ _lastTimeH = _gameRef->_timer;
+ if (_offsetLeft < _targetOffsetLeft) {
+ _offsetLeft += _scrollPixelsH;
+ _offsetLeft = MIN(_offsetLeft, _targetOffsetLeft);
+ } else if (_offsetLeft > _targetOffsetLeft) {
+ _offsetLeft -= _scrollPixelsH;
+ _offsetLeft = MAX(_offsetLeft, _targetOffsetLeft);
+ }
+ }
+
+ // adjust vertical scroll
+ if (_gameRef->_timer - _lastTimeV >= _scrollTimeV) {
+ _lastTimeV = _gameRef->_timer;
+ if (_offsetTop < _targetOffsetTop) {
+ _offsetTop += _scrollPixelsV;
+ _offsetTop = MIN(_offsetTop, _targetOffsetTop);
+ } else if (_offsetTop > _targetOffsetTop) {
+ _offsetTop -= _scrollPixelsV;
+ _offsetTop = MAX(_offsetTop, _targetOffsetTop);
+ }
+ }
+
+ if (_offsetTop == _targetOffsetTop && _offsetLeft == _targetOffsetLeft) {
+ _ready = true;
+ }
+ } else {
+ _ready = true; // not scrolling, i.e. always ready
+ }
+ }
+
+
+
+
+ //////////////////////////////////////////////////////////////////////////
+ int viewportWidth, viewportHeight;
+ getViewportSize(&viewportWidth, &viewportHeight);
+
+ int viewportX, viewportY;
+ getViewportOffset(&viewportX, &viewportY);
+
+ int scrollableX = _width - viewportWidth;
+ int scrollableY = _height - viewportHeight;
+
+ double widthRatio = scrollableX <= 0 ? 0 : ((double)(_offsetLeft) / (double)scrollableX);
+ double heightRatio = scrollableY <= 0 ? 0 : ((double)(_offsetTop) / (double)scrollableY);
+
+ int origX, origY;
+ _gameRef->getOffset(&origX, &origY);
+
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // *** display/update everything
+ _gameRef->_renderer->setup2D();
+
+ // for each layer
+ /* int mainOffsetX = 0; */
+ /* int mainOffsetY = 0; */
+
+ for (uint32 j = 0; j < _layers.size(); j++) {
+ if (!_layers[j]->_active) {
+ continue;
+ }
+
+ // make layer exclusive
+ if (!doUpdate) {
+ if (_layers[j]->_closeUp && !_gameRef->_editorMode) {
+ if (!_shieldWindow) {
+ _shieldWindow = new UIWindow(_gameRef);
+ }
+ if (_shieldWindow) {
+ _shieldWindow->_posX = _shieldWindow->_posY = 0;
+ _shieldWindow->_width = _gameRef->_renderer->_width;
+ _shieldWindow->_height = _gameRef->_renderer->_height;
+ _shieldWindow->display();
+ }
+ }
+ }
+
+ if (_paralaxScrolling) {
+ int offsetX = (int)(widthRatio * (_layers[j]->_width - viewportWidth) - viewportX);
+ int offsetY = (int)(heightRatio * (_layers[j]->_height - viewportHeight) - viewportY);
+ _gameRef->setOffset(offsetX, offsetY);
+
+ _gameRef->_offsetPercentX = (float)offsetX / ((float)_layers[j]->_width - viewportWidth) * 100.0f;
+ _gameRef->_offsetPercentY = (float)offsetY / ((float)_layers[j]->_height - viewportHeight) * 100.0f;
+
+ //_gameRef->QuickMessageForm("%d %f", OffsetX+ViewportX, _gameRef->_offsetPercentX);
+ } else {
+ _gameRef->setOffset(_offsetLeft - viewportX, _offsetTop - viewportY);
+
+ _gameRef->_offsetPercentX = (float)(_offsetLeft - viewportX) / ((float)_layers[j]->_width - viewportWidth) * 100.0f;
+ _gameRef->_offsetPercentY = (float)(_offsetTop - viewportY) / ((float)_layers[j]->_height - viewportHeight) * 100.0f;
+ }
+
+
+ // for each node
+ for (uint32 k = 0; k < _layers[j]->_nodes.size(); k++) {
+ AdSceneNode *node = _layers[j]->_nodes[k];
+ switch (node->_type) {
+ case OBJECT_ENTITY:
+ if (node->_entity->_active && (_gameRef->_editorMode || !node->_entity->_editorOnly)) {
+ _gameRef->_renderer->setup2D();
+
+ if (doUpdate) {
+ node->_entity->update();
+ } else {
+ node->_entity->display();
+ }
+ }
+ break;
+
+ case OBJECT_REGION: {
+ if (node->_region->_blocked) {
+ break;
+ }
+ if (node->_region->_decoration) {
+ break;
+ }
+
+ if (!doUpdate) {
+ displayRegionContent(node->_region);
+ }
+ }
+ break;
+ default:
+ error("AdScene::TraverseNodes - Unhandled enum");
+ break;
+ } // switch
+ } // each node
+
+ // display/update all objects which are off-regions
+ if (_layers[j]->_main) {
+ if (doUpdate) {
+ updateFreeObjects();
+ } else {
+ displayRegionContent(NULL);
+ }
+ }
+ } // each layer
+
+
+ // restore state
+ _gameRef->setOffset(origX, origY);
+ _gameRef->_renderer->setup2D();
+
+ // display/update fader
+ if (_fader) {
+ if (doUpdate) {
+ _fader->update();
+ } else {
+ _fader->display();
+ }
+ }
+
+ if (popViewport) {
+ _gameRef->popViewport();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::display() {
+ return traverseNodes(false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::updateFreeObjects() {
+ AdGame *adGame = (AdGame *)_gameRef;
+ // 3D-code removed
+ // bool is3DSet;
+
+ // *** update all active objects
+ // is3DSet = false;
+ for (uint32 i = 0; i < adGame->_objects.size(); i++) {
+ if (!adGame->_objects[i]->_active) {
+ continue;
+ }
+ // 3D-code removed
+ adGame->_objects[i]->update();
+ adGame->_objects[i]->_drawn = false;
+ }
+
+
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (!_objects[i]->_active) {
+ continue;
+ }
+
+ _objects[i]->update();
+ _objects[i]->_drawn = false;
+ }
+
+
+ if (_autoScroll && _gameRef->_mainObject != NULL) {
+ scrollToObject(_gameRef->_mainObject);
+ }
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::displayRegionContent(AdRegion *region, bool display3DOnly) {
+ AdGame *adGame = (AdGame *)_gameRef;
+ BaseArray<AdObject *> objects;
+ AdObject *obj;
+
+ // global objects
+ for (uint32 i = 0; i < adGame->_objects.size(); i++) {
+ obj = adGame->_objects[i];
+ if (obj->_active && !obj->_drawn && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) {
+ objects.add(obj);
+ }
+ }
+
+ // scene objects
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ obj = _objects[i];
+ if (obj->_active && !obj->_editorOnly && !obj->_drawn && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) {
+ objects.add(obj);
+ }
+ }
+
+ // sort by _posY
+ Common::sort(objects.begin(), objects.end(), AdScene::compareObjs);
+
+ // display them
+ for (uint32 i = 0; i < objects.size(); i++) {
+ obj = objects[i];
+
+ if (display3DOnly && !obj->_is3D) {
+ continue;
+ }
+
+ _gameRef->_renderer->setup2D();
+
+ if (_gameRef->_editorMode || !obj->_editorOnly) {
+ obj->display();
+ }
+ obj->_drawn = true;
+ }
+
+
+ // display design only objects
+ if (!display3DOnly) {
+ if (_gameRef->_editorMode && region == NULL) {
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_active && _objects[i]->_editorOnly) {
+ _objects[i]->display();
+ _objects[i]->_drawn = true;
+ }
+ }
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int AdScene::compareObjs(const void *obj1, const void *obj2) {
+ const AdObject *object1 = *(const AdObject *const *)obj1;
+ const AdObject *object2 = *(const AdObject *const *)obj2;
+
+ if (object1->_posY < object2->_posY) {
+ return -1;
+ } else if (object1->_posY > object2->_posY) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::displayRegionContentOld(AdRegion *region) {
+ AdGame *adGame = (AdGame *)_gameRef;
+ AdObject *obj;
+
+ // display all objects in region sorted by _posY
+ do {
+ obj = NULL;
+ int minY = INT_MAX;
+
+ // global objects
+ for (uint32 i = 0; i < adGame->_objects.size(); i++) {
+ if (adGame->_objects[i]->_active && !adGame->_objects[i]->_drawn && adGame->_objects[i]->_posY < minY && (adGame->_objects[i]->_stickRegion == region || region == NULL || (adGame->_objects[i]->_stickRegion == NULL && region->pointInRegion(adGame->_objects[i]->_posX, adGame->_objects[i]->_posY)))) {
+ obj = adGame->_objects[i];
+ minY = adGame->_objects[i]->_posY;
+ }
+ }
+
+ // scene objects
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_active && !_objects[i]->_editorOnly && !_objects[i]->_drawn && _objects[i]->_posY < minY && (_objects[i]->_stickRegion == region || region == NULL || (_objects[i]->_stickRegion == NULL && region->pointInRegion(_objects[i]->_posX, _objects[i]->_posY)))) {
+ obj = _objects[i];
+ minY = _objects[i]->_posY;
+ }
+ }
+
+
+ if (obj != NULL) {
+ _gameRef->_renderer->setup2D();
+
+ if (_gameRef->_editorMode || !obj->_editorOnly) {
+ obj->display();
+ }
+ obj->_drawn = true;
+ }
+ } while (obj != NULL);
+
+
+ // design only objects
+ if (_gameRef->_editorMode && region == NULL) {
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_active && _objects[i]->_editorOnly) {
+ _objects[i]->display();
+ _objects[i]->_drawn = true;
+ }
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::update() {
+ return traverseNodes(true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::scrollTo(int offsetX, int offsetY) {
+ int viewportWidth, viewportHeight;
+ getViewportSize(&viewportWidth, &viewportHeight);
+
+ int origOffsetLeft = _targetOffsetLeft;
+ int origOffsetTop = _targetOffsetTop;
+
+ _targetOffsetLeft = MAX(0, offsetX - viewportWidth / 2);
+ _targetOffsetLeft = MIN(_targetOffsetLeft, _width - viewportWidth);
+
+ _targetOffsetTop = MAX(0, offsetY - viewportHeight / 2);
+ _targetOffsetTop = MIN(_targetOffsetTop, _height - viewportHeight);
+
+
+ if (_gameRef->_mainObject && _gameRef->_mainObject->_is3D) {
+ if (abs(origOffsetLeft - _targetOffsetLeft) < 5) {
+ _targetOffsetLeft = origOffsetLeft;
+ }
+ if (abs(origOffsetTop - _targetOffsetTop) < 5) {
+ _targetOffsetTop = origOffsetTop;
+ }
+ //_targetOffsetTop = 0;
+ }
+
+ _ready = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::scrollToObject(BaseObject *object) {
+ if (object) {
+ scrollTo(object->_posX, object->_posY - object->getHeight() / 2);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::skipToObject(BaseObject *object) {
+ if (object) {
+ skipTo(object->_posX, object->_posY - object->getHeight() / 2);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::skipTo(int offsetX, int offsetY) {
+ int viewportWidth, viewportHeight;
+ getViewportSize(&viewportWidth, &viewportHeight);
+
+ _offsetLeft = MAX(0, offsetX - viewportWidth / 2);
+ _offsetLeft = MIN(_offsetLeft, _width - viewportWidth);
+
+ _offsetTop = MAX(0, offsetY - viewportHeight / 2);
+ _offsetTop = MIN(_offsetTop, _height - viewportHeight);
+
+ _targetOffsetLeft = _offsetLeft;
+ _targetOffsetTop = _offsetTop;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // LoadActor
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "LoadActor") == 0) {
+ stack->correctParams(1);
+ AdActor *act = new AdActor(_gameRef);
+ if (act && DID_SUCCEED(act->loadFile(stack->pop()->getString()))) {
+ addObject(act);
+ stack->pushNative(act, true);
+ } else {
+ delete act;
+ act = NULL;
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadEntity") == 0) {
+ stack->correctParams(1);
+ AdEntity *ent = new AdEntity(_gameRef);
+ if (ent && DID_SUCCEED(ent->loadFile(stack->pop()->getString()))) {
+ addObject(ent);
+ stack->pushNative(ent, true);
+ } else {
+ delete ent;
+ ent = NULL;
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateEntity") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdEntity *ent = new AdEntity(_gameRef);
+ addObject(ent);
+ if (!val->isNULL()) {
+ ent->setName(val->getString());
+ }
+ stack->pushNative(ent, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // UnloadObject / UnloadActor / UnloadEntity / UnloadActor3D / DeleteEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "UnloadObject") == 0 || strcmp(name, "UnloadActor") == 0 || strcmp(name, "UnloadEntity") == 0 || strcmp(name, "UnloadActor3D") == 0 || strcmp(name, "DeleteEntity") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ AdObject *obj = (AdObject *)val->getNative();
+ removeObject(obj);
+ if (val->getType() == VAL_VARIABLE_REF) {
+ val->setNULL();
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SkipTo
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SkipTo") == 0) {
+ stack->correctParams(2);
+ ScValue *val1 = stack->pop();
+ ScValue *val2 = stack->pop();
+ if (val1->isNative()) {
+ skipToObject((BaseObject *)val1->getNative());
+ } else {
+ skipTo(val1->getInt(), val2->getInt());
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollTo / ScrollToAsync
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScrollTo") == 0 || strcmp(name, "ScrollToAsync") == 0) {
+ stack->correctParams(2);
+ ScValue *val1 = stack->pop();
+ ScValue *val2 = stack->pop();
+ if (val1->isNative()) {
+ scrollToObject((BaseObject *)val1->getNative());
+ } else {
+ scrollTo(val1->getInt(), val2->getInt());
+ }
+ if (strcmp(name, "ScrollTo") == 0) {
+ script->waitForExclusive(this);
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetLayer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetLayer") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ if (val->isInt()) {
+ int layer = val->getInt();
+ if (layer < 0 || layer >= (int32)_layers.size()) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_layers[layer], true);
+ }
+ } else {
+ const char *layerName = val->getString();
+ bool layerFound = false;
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ if (scumm_stricmp(layerName, _layers[i]->getName()) == 0) {
+ stack->pushNative(_layers[i], true);
+ layerFound = true;
+ break;
+ }
+ }
+ if (!layerFound) {
+ stack->pushNULL();
+ }
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetWaypointGroup
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetWaypointGroup") == 0) {
+ stack->correctParams(1);
+ int group = stack->pop()->getInt();
+ if (group < 0 || group >= (int32)_waypointGroups.size()) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_waypointGroups[group], true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetNode
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetNode") == 0) {
+ stack->correctParams(1);
+ const char *nodeName = stack->pop()->getString();
+
+ BaseObject *node = getNodeByName(nodeName);
+ if (node) {
+ stack->pushNative((BaseScriptable *)node, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFreeNode
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFreeNode") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdObject *ret = NULL;
+ if (val->isInt()) {
+ int index = val->getInt();
+ if (index >= 0 && index < (int32)_objects.size()) {
+ ret = _objects[index];
+ }
+ } else {
+ const char *nodeName = val->getString();
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i] && _objects[i]->getName() && scumm_stricmp(_objects[i]->getName(), nodeName) == 0) {
+ ret = _objects[i];
+ break;
+ }
+ }
+ }
+ if (ret) {
+ stack->pushNative(ret, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetRegionAt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetRegionAt") == 0) {
+ stack->correctParams(3);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+ ScValue *val = stack->pop();
+
+ bool includeDecors = false;
+ if (!val->isNULL()) {
+ includeDecors = val->getBool();
+ }
+
+ if (_mainLayer) {
+ for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) {
+ AdSceneNode *node = _mainLayer->_nodes[i];
+ if (node->_type == OBJECT_REGION && node->_region->_active && node->_region->pointInRegion(x, y)) {
+ if (node->_region->_decoration && !includeDecors) {
+ continue;
+ }
+
+ stack->pushNative(node->_region, true);
+ return STATUS_OK;
+ }
+ }
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsBlockedAt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsBlockedAt") == 0) {
+ stack->correctParams(2);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+
+ stack->pushBool(isBlockedAt(x, y));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsWalkableAt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsWalkableAt") == 0) {
+ stack->correctParams(2);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+
+ stack->pushBool(isWalkableAt(x, y));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetScaleAt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetScaleAt") == 0) {
+ stack->correctParams(2);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+
+ stack->pushFloat(getZoomAt(x, y));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetRotationAt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetRotationAt") == 0) {
+ stack->correctParams(2);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+
+ stack->pushFloat(getRotationAt(x, y));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsScrolling
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsScrolling") == 0) {
+ stack->correctParams(0);
+ bool ret = false;
+ if (_autoScroll) {
+ if (_targetOffsetLeft != _offsetLeft || _targetOffsetTop != _offsetTop) {
+ ret = true;
+ }
+ }
+
+ stack->pushBool(ret);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FadeOut / FadeOutAsync
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FadeOut") == 0 || strcmp(name, "FadeOutAsync") == 0) {
+ stack->correctParams(5);
+ uint32 duration = stack->pop()->getInt(500);
+ byte red = stack->pop()->getInt(0);
+ byte green = stack->pop()->getInt(0);
+ byte blue = stack->pop()->getInt(0);
+ byte alpha = stack->pop()->getInt(0xFF);
+
+ _fader->fadeOut(BYTETORGBA(red, green, blue, alpha), duration);
+ if (strcmp(name, "FadeOutAsync") != 0) {
+ script->waitFor(_fader);
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FadeIn / FadeInAsync
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FadeIn") == 0 || strcmp(name, "FadeInAsync") == 0) {
+ stack->correctParams(5);
+ uint32 duration = stack->pop()->getInt(500);
+ byte red = stack->pop()->getInt(0);
+ byte green = stack->pop()->getInt(0);
+ byte blue = stack->pop()->getInt(0);
+ byte alpha = stack->pop()->getInt(0xFF);
+
+ _fader->fadeIn(BYTETORGBA(red, green, blue, alpha), duration);
+ if (strcmp(name, "FadeInAsync") != 0) {
+ script->waitFor(_fader);
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFadeColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFadeColor") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_fader->getCurrentColor());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsPointInViewport
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsPointInViewport") == 0) {
+ stack->correctParams(2);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+ stack->pushBool(pointInViewport(x, y));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetViewport
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetViewport") == 0) {
+ stack->correctParams(4);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+ int width = stack->pop()->getInt();
+ int height = stack->pop()->getInt();
+
+ if (width <= 0) {
+ width = _gameRef->_renderer->_width;
+ }
+ if (height <= 0) {
+ height = _gameRef->_renderer->_height;
+ }
+
+ if (!_viewport) {
+ _viewport = new BaseViewport(_gameRef);
+ }
+ if (_viewport) {
+ _viewport->setRect(x, y, x + width, y + height);
+ }
+
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddLayer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddLayer") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdLayer *layer = new AdLayer(_gameRef);
+ if (!val->isNULL()) {
+ layer->setName(val->getString());
+ }
+ if (_mainLayer) {
+ layer->_width = _mainLayer->_width;
+ layer->_height = _mainLayer->_height;
+ }
+ _layers.add(layer);
+ _gameRef->registerObject(layer);
+
+ stack->pushNative(layer, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InsertLayer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InsertLayer") == 0) {
+ stack->correctParams(2);
+ int index = stack->pop()->getInt();
+ ScValue *val = stack->pop();
+
+ AdLayer *layer = new AdLayer(_gameRef);
+ if (!val->isNULL()) {
+ layer->setName(val->getString());
+ }
+ if (_mainLayer) {
+ layer->_width = _mainLayer->_width;
+ layer->_height = _mainLayer->_height;
+ }
+ if (index < 0) {
+ index = 0;
+ }
+ if (index <= (int32)_layers.size() - 1) {
+ _layers.insert_at(index, layer);
+ } else {
+ _layers.add(layer);
+ }
+
+ _gameRef->registerObject(layer);
+
+ stack->pushNative(layer, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteLayer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteLayer") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ AdLayer *toDelete = NULL;
+ if (val->isNative()) {
+ BaseScriptable *temp = val->getNative();
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ if (_layers[i] == temp) {
+ toDelete = _layers[i];
+ break;
+ }
+ }
+ } else {
+ int index = val->getInt();
+ if (index >= 0 && index < (int32)_layers.size()) {
+ toDelete = _layers[index];
+ }
+ }
+ if (toDelete == NULL) {
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ if (toDelete->_main) {
+ script->runtimeError("Scene.DeleteLayer - cannot delete main scene layer");
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ if (_layers[i] == toDelete) {
+ _layers.remove_at(i);
+ _gameRef->unregisterObject(toDelete);
+ break;
+ }
+ }
+ stack->pushBool(true);
+ return STATUS_OK;
+ } else {
+ return BaseObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdScene::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("scene");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumLayers (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumLayers") {
+ _scValue->setInt(_layers.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumWaypointGroups (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumWaypointGroups") {
+ _scValue->setInt(_waypointGroups.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MainLayer (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MainLayer") {
+ if (_mainLayer) {
+ _scValue->setNative(_mainLayer, true);
+ } else {
+ _scValue->setNULL();
+ }
+
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumFreeNodes (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumFreeNodes") {
+ _scValue->setInt(_objects.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MouseX (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MouseX") {
+ int viewportX;
+ getViewportOffset(&viewportX);
+
+ _scValue->setInt(_gameRef->_mousePos.x + _offsetLeft - viewportX);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MouseY (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MouseY") {
+ int viewportY;
+ getViewportOffset(NULL, &viewportY);
+
+ _scValue->setInt(_gameRef->_mousePos.y + _offsetTop - viewportY);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AutoScroll
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AutoScroll") {
+ _scValue->setBool(_autoScroll);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PersistentState
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "PersistentState") {
+ _scValue->setBool(_persistentState);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PersistentStateSprites
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "PersistentStateSprites") {
+ _scValue->setBool(_persistentStateSprites);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollPixelsX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScrollPixelsX") {
+ _scValue->setInt(_scrollPixelsH);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollPixelsY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScrollPixelsY") {
+ _scValue->setInt(_scrollPixelsV);
+ return _scValue;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollSpeedX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScrollSpeedX") {
+ _scValue->setInt(_scrollTimeH);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollSpeedY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScrollSpeedY") {
+ _scValue->setInt(_scrollTimeV);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // OffsetX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "OffsetX") {
+ _scValue->setInt(_offsetLeft);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // OffsetY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "OffsetY") {
+ _scValue->setInt(_offsetTop);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Width (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Width") {
+ if (_mainLayer) {
+ _scValue->setInt(_mainLayer->_width);
+ } else {
+ _scValue->setInt(0);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Height (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Height") {
+ if (_mainLayer) {
+ _scValue->setInt(_mainLayer->_height);
+ } else {
+ _scValue->setInt(0);
+ }
+ return _scValue;
+ } else {
+ return BaseObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AutoScroll
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AutoScroll") == 0) {
+ _autoScroll = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PersistentState
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PersistentState") == 0) {
+ _persistentState = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PersistentStateSprites
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PersistentStateSprites") == 0) {
+ _persistentStateSprites = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollPixelsX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScrollPixelsX") == 0) {
+ _scrollPixelsH = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollPixelsY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScrollPixelsY") == 0) {
+ _scrollPixelsV = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollSpeedX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScrollSpeedX") == 0) {
+ _scrollTimeH = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScrollSpeedY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScrollSpeedY") == 0) {
+ _scrollTimeV = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // OffsetX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "OffsetX") == 0) {
+ _offsetLeft = value->getInt();
+
+ int viewportWidth, viewportHeight;
+ getViewportSize(&viewportWidth, &viewportHeight);
+
+ _offsetLeft = MAX(0, _offsetLeft - viewportWidth / 2);
+ _offsetLeft = MIN(_offsetLeft, _width - viewportWidth);
+ _targetOffsetLeft = _offsetLeft;
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // OffsetY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "OffsetY") == 0) {
+ _offsetTop = value->getInt();
+
+ int viewportWidth, viewportHeight;
+ getViewportSize(&viewportWidth, &viewportHeight);
+
+ _offsetTop = MAX(0, _offsetTop - viewportHeight / 2);
+ _offsetTop = MIN(_offsetTop, _height - viewportHeight);
+ _targetOffsetTop = _offsetTop;
+
+ return STATUS_OK;
+ } else {
+ return BaseObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdScene::scToString() {
+ return "[scene object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::addObject(AdObject *object) {
+ _objects.add(object);
+ return _gameRef->registerObject(object);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::removeObject(AdObject *object) {
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i] == object) {
+ _objects.remove_at(i);
+ return _gameRef->unregisterObject(object);
+ }
+ }
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "SCENE {\n");
+
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+
+ if (_persistentState) {
+ buffer->putTextIndent(indent + 2, "PERSISTENT_STATE=%s\n", _persistentState ? "TRUE" : "FALSE");
+ }
+
+ if (!_persistentStateSprites) {
+ buffer->putTextIndent(indent + 2, "PERSISTENT_STATE_SPRITES=%s\n", _persistentStateSprites ? "TRUE" : "FALSE");
+ }
+
+
+ // scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // properties
+ if (_scProp) {
+ _scProp->saveAsText(buffer, indent + 2);
+ }
+
+ // viewport
+ if (_viewport) {
+ Rect32 *rc = _viewport->getRect();
+ buffer->putTextIndent(indent + 2, "VIEWPORT { %d, %d, %d, %d }\n", rc->left, rc->top, rc->right, rc->bottom);
+ }
+
+
+
+ // editor settings
+ buffer->putTextIndent(indent + 2, "; ----- editor settings\n");
+ buffer->putTextIndent(indent + 2, "EDITOR_MARGIN_H=%d\n", _editorMarginH);
+ buffer->putTextIndent(indent + 2, "EDITOR_MARGIN_V=%d\n", _editorMarginV);
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_FRAME { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColFrame), RGBCOLGetG(_editorColFrame), RGBCOLGetB(_editorColFrame), RGBCOLGetA(_editorColFrame));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_ENTITY_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColEntitySel), RGBCOLGetG(_editorColEntitySel), RGBCOLGetB(_editorColEntitySel), RGBCOLGetA(_editorColEntitySel));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_REGION_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColRegionSel), RGBCOLGetG(_editorColRegionSel), RGBCOLGetB(_editorColRegionSel), RGBCOLGetA(_editorColRegionSel));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_BLOCKED_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColBlockedSel), RGBCOLGetG(_editorColBlockedSel), RGBCOLGetB(_editorColBlockedSel), RGBCOLGetA(_editorColBlockedSel));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_DECORATION_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColDecorSel), RGBCOLGetG(_editorColDecorSel), RGBCOLGetB(_editorColDecorSel), RGBCOLGetA(_editorColDecorSel));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_WAYPOINTS_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColWaypointsSel), RGBCOLGetG(_editorColWaypointsSel), RGBCOLGetB(_editorColWaypointsSel), RGBCOLGetA(_editorColWaypointsSel));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_ENTITY { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColEntity), RGBCOLGetG(_editorColEntity), RGBCOLGetB(_editorColEntity), RGBCOLGetA(_editorColEntity));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_REGION { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColRegion), RGBCOLGetG(_editorColRegion), RGBCOLGetB(_editorColRegion), RGBCOLGetA(_editorColRegion));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_DECORATION { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColDecor), RGBCOLGetG(_editorColDecor), RGBCOLGetB(_editorColDecor), RGBCOLGetA(_editorColDecor));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_BLOCKED { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColBlocked), RGBCOLGetG(_editorColBlocked), RGBCOLGetB(_editorColBlocked), RGBCOLGetA(_editorColBlocked));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_WAYPOINTS { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColWaypoints), RGBCOLGetG(_editorColWaypoints), RGBCOLGetB(_editorColWaypoints), RGBCOLGetA(_editorColWaypoints));
+ buffer->putTextIndent(indent + 2, "EDITOR_COLOR_SCALE { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColScale), RGBCOLGetG(_editorColScale), RGBCOLGetB(_editorColScale), RGBCOLGetA(_editorColScale));
+
+ buffer->putTextIndent(indent + 2, "EDITOR_SHOW_REGIONS=%s\n", _editorShowRegions ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "EDITOR_SHOW_BLOCKED=%s\n", _editorShowBlocked ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "EDITOR_SHOW_DECORATION=%s\n", _editorShowDecor ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "EDITOR_SHOW_ENTITIES=%s\n", _editorShowEntities ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "EDITOR_SHOW_SCALE=%s\n", _editorShowScale ? "TRUE" : "FALSE");
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ // waypoints
+ buffer->putTextIndent(indent + 2, "; ----- waypoints\n");
+ for (uint32 i = 0; i < _waypointGroups.size(); i++) {
+ _waypointGroups[i]->saveAsText(buffer, indent + 2);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // layers
+ buffer->putTextIndent(indent + 2, "; ----- layers\n");
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ _layers[i]->saveAsText(buffer, indent + 2);
+ }
+
+ // scale levels
+ buffer->putTextIndent(indent + 2, "; ----- scale levels\n");
+ for (uint32 i = 0; i < _scaleLevels.size(); i++) {
+ _scaleLevels[i]->saveAsText(buffer, indent + 2);
+ }
+
+ // rotation levels
+ buffer->putTextIndent(indent + 2, "; ----- rotation levels\n");
+ for (uint32 i = 0; i < _rotLevels.size(); i++) {
+ _rotLevels[i]->saveAsText(buffer, indent + 2);
+ }
+
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // free entities
+ buffer->putTextIndent(indent + 2, "; ----- free entities\n");
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_type == OBJECT_ENTITY) {
+ _objects[i]->saveAsText(buffer, indent + 2);
+
+ }
+ }
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::sortScaleLevels() {
+ if (_scaleLevels.size() == 0) {
+ return STATUS_OK;
+ }
+ bool changed;
+ do {
+ changed = false;
+ for (uint32 i = 0; i < _scaleLevels.size() - 1; i++) {
+ if (_scaleLevels[i]->_posY > _scaleLevels[i + 1]->_posY) {
+ AdScaleLevel *sl = _scaleLevels[i];
+ _scaleLevels[i] = _scaleLevels[i + 1];
+ _scaleLevels[i + 1] = sl;
+
+ changed = true;
+ }
+ }
+
+ } while (changed);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::sortRotLevels() {
+ if (_rotLevels.size() == 0) {
+ return STATUS_OK;
+ }
+ bool changed;
+ do {
+ changed = false;
+ for (uint32 i = 0; i < _rotLevels.size() - 1; i++) {
+ if (_rotLevels[i]->_posX > _rotLevels[i + 1]->_posX) {
+ AdRotLevel *rl = _rotLevels[i];
+ _rotLevels[i] = _rotLevels[i + 1];
+ _rotLevels[i + 1] = rl;
+
+ changed = true;
+ }
+ }
+
+ } while (changed);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float AdScene::getScaleAt(int Y) {
+ AdScaleLevel *prev = NULL;
+ AdScaleLevel *next = NULL;
+
+ for (uint32 i = 0; i < _scaleLevels.size(); i++) {
+ /* AdScaleLevel *xxx = _scaleLevels[i];*/
+ /* int j = _scaleLevels.size(); */
+ if (_scaleLevels[i]->_posY < Y) {
+ prev = _scaleLevels[i];
+ } else {
+ next = _scaleLevels[i];
+ break;
+ }
+ }
+
+ if (prev == NULL || next == NULL) {
+ return 100;
+ }
+
+ int delta_y = next->_posY - prev->_posY;
+ float delta_scale = next->_scale - prev->_scale;
+ Y -= prev->_posY;
+
+ float percent = (float)Y / ((float)delta_y / 100.0f);
+ return prev->_scale + delta_scale / 100 * percent;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::persist(BasePersistenceManager *persistMgr) {
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_autoScroll));
+ persistMgr->transfer(TMEMBER(_editorColBlocked));
+ persistMgr->transfer(TMEMBER(_editorColBlockedSel));
+ persistMgr->transfer(TMEMBER(_editorColDecor));
+ persistMgr->transfer(TMEMBER(_editorColDecorSel));
+ persistMgr->transfer(TMEMBER(_editorColEntity));
+ persistMgr->transfer(TMEMBER(_editorColEntitySel));
+ persistMgr->transfer(TMEMBER(_editorColFrame));
+ persistMgr->transfer(TMEMBER(_editorColRegion));
+ persistMgr->transfer(TMEMBER(_editorColRegionSel));
+ persistMgr->transfer(TMEMBER(_editorColScale));
+ persistMgr->transfer(TMEMBER(_editorColWaypoints));
+ persistMgr->transfer(TMEMBER(_editorColWaypointsSel));
+ persistMgr->transfer(TMEMBER(_editorMarginH));
+ persistMgr->transfer(TMEMBER(_editorMarginV));
+ persistMgr->transfer(TMEMBER(_editorShowBlocked));
+ persistMgr->transfer(TMEMBER(_editorShowDecor));
+ persistMgr->transfer(TMEMBER(_editorShowEntities));
+ persistMgr->transfer(TMEMBER(_editorShowRegions));
+ persistMgr->transfer(TMEMBER(_editorShowScale));
+ persistMgr->transfer(TMEMBER(_fader));
+ persistMgr->transfer(TMEMBER(_height));
+ persistMgr->transfer(TMEMBER(_initialized));
+ persistMgr->transfer(TMEMBER(_lastTimeH));
+ persistMgr->transfer(TMEMBER(_lastTimeV));
+ _layers.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_mainLayer));
+ _objects.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_offsetLeft));
+ persistMgr->transfer(TMEMBER(_offsetTop));
+ persistMgr->transfer(TMEMBER(_paralaxScrolling));
+ persistMgr->transfer(TMEMBER(_persistentState));
+ persistMgr->transfer(TMEMBER(_persistentStateSprites));
+ persistMgr->transfer(TMEMBER(_pfMaxTime));
+ _pfPath.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_pfPointsNum));
+ persistMgr->transfer(TMEMBER(_pfReady));
+ persistMgr->transfer(TMEMBER(_pfRequester));
+ persistMgr->transfer(TMEMBER(_pfTarget));
+ persistMgr->transfer(TMEMBER(_pfTargetPath));
+ _rotLevels.persist(persistMgr);
+ _scaleLevels.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_scrollPixelsH));
+ persistMgr->transfer(TMEMBER(_scrollPixelsV));
+ persistMgr->transfer(TMEMBER(_scrollTimeH));
+ persistMgr->transfer(TMEMBER(_scrollTimeV));
+ persistMgr->transfer(TMEMBER(_shieldWindow));
+ persistMgr->transfer(TMEMBER(_targetOffsetLeft));
+ persistMgr->transfer(TMEMBER(_targetOffsetTop));
+ _waypointGroups.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_viewport));
+ persistMgr->transfer(TMEMBER(_width));
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::afterLoad() {
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::correctTargetPoint2(int startX, int startY, int *targetX, int *targetY, bool checkFreeObjects, BaseObject *requester) {
+ double xStep, yStep, x, y;
+ int xLength, yLength, xCount, yCount;
+ int x1, y1, x2, y2;
+
+ x1 = *targetX;
+ y1 = *targetY;
+ x2 = startX;
+ y2 = startY;
+
+
+ xLength = abs(x2 - x1);
+ yLength = abs(y2 - y1);
+
+ if (xLength > yLength) {
+
+ yStep = fabs((double)(y2 - y1) / (double)(x2 - x1));
+ y = y1;
+
+ for (xCount = x1; xCount < x2; xCount++) {
+ if (isWalkableAt(xCount, (int)y, checkFreeObjects, requester)) {
+ *targetX = xCount;
+ *targetY = (int)y;
+ return STATUS_OK;
+ }
+ y += yStep;
+ }
+ } else {
+
+ xStep = fabs((double)(x2 - x1) / (double)(y2 - y1));
+ x = x1;
+
+ for (yCount = y1; yCount < y2; yCount++) {
+ if (isWalkableAt((int)x, yCount, checkFreeObjects, requester)) {
+ *targetX = (int)x;
+ *targetY = yCount;
+ return STATUS_OK;
+ }
+ x += xStep;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::correctTargetPoint(int startX, int startY, int *argX, int *argY, bool checkFreeObjects, BaseObject *requester) {
+ int x = *argX;
+ int y = *argY;
+
+ if (isWalkableAt(x, y, checkFreeObjects, requester) || !_mainLayer) {
+ return STATUS_OK;
+ }
+
+ // right
+ int lengthRight = 0;
+ bool foundRight = false;
+ for (x = *argX, y = *argY; x < _mainLayer->_width; x++, lengthRight++) {
+ if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x - 5, y, checkFreeObjects, requester)) {
+ foundRight = true;
+ break;
+ }
+ }
+
+ // left
+ int lengthLeft = 0;
+ bool foundLeft = false;
+ for (x = *argX, y = *argY; x >= 0; x--, lengthLeft--) {
+ if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x + 5, y, checkFreeObjects, requester)) {
+ foundLeft = true;
+ break;
+ }
+ }
+
+ // up
+ int lengthUp = 0;
+ bool foundUp = false;
+ for (x = *argX, y = *argY; y >= 0; y--, lengthUp--) {
+ if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x, y + 5, checkFreeObjects, requester)) {
+ foundUp = true;
+ break;
+ }
+ }
+
+ // down
+ int lengthDown = 0;
+ bool foundDown = false;
+ for (x = *argX, y = *argY; y < _mainLayer->_height; y++, lengthDown++) {
+ if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x, y - 5, checkFreeObjects, requester)) {
+ foundDown = true;
+ break;
+ }
+ }
+
+ if (!foundLeft && !foundRight && !foundUp && !foundDown) {
+ return STATUS_OK;
+ }
+
+ int offsetX = INT_MAX, offsetY = INT_MAX;
+
+ if (foundLeft && foundRight) {
+ if (abs(lengthLeft) < abs(lengthRight)) {
+ offsetX = lengthLeft;
+ } else {
+ offsetX = lengthRight;
+ }
+ } else if (foundLeft) {
+ offsetX = lengthLeft;
+ } else if (foundRight) {
+ offsetX = lengthRight;
+ }
+
+ if (foundUp && foundDown) {
+ if (abs(lengthUp) < abs(lengthDown)) {
+ offsetY = lengthUp;
+ } else {
+ offsetY = lengthDown;
+ }
+ } else if (foundUp) {
+ offsetY = lengthUp;
+ } else if (foundDown) {
+ offsetY = lengthDown;
+ }
+
+ if (abs(offsetX) < abs(offsetY)) {
+ *argX = *argX + offsetX;
+ } else {
+ *argY = *argY + offsetY;
+ }
+
+ if (!isWalkableAt(*argX, *argY)) {
+ return correctTargetPoint2(startX, startY, argX, argY, checkFreeObjects, requester);
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::pfPointsStart() {
+ _pfPointsNum = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::pfPointsAdd(int x, int y, int distance) {
+ if (_pfPointsNum >= (int32)_pfPath.size()) {
+ _pfPath.add(new AdPathPoint(x, y, distance));
+ } else {
+ _pfPath[_pfPointsNum]->x = x;
+ _pfPath[_pfPointsNum]->y = y;
+ _pfPath[_pfPointsNum]->_distance = distance;
+ _pfPath[_pfPointsNum]->_marked = false;
+ _pfPath[_pfPointsNum]->_origin = NULL;
+ }
+
+ _pfPointsNum++;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::getViewportOffset(int *offsetX, int *offsetY) {
+ AdGame *adGame = (AdGame *)_gameRef;
+ if (_viewport && !_gameRef->_editorMode) {
+ if (offsetX) {
+ *offsetX = _viewport->_offsetX;
+ }
+ if (offsetY) {
+ *offsetY = _viewport->_offsetY;
+ }
+ } else if (adGame->_sceneViewport && !_gameRef->_editorMode) {
+ if (offsetX) {
+ *offsetX = adGame->_sceneViewport->_offsetX;
+ }
+ if (offsetY) {
+ *offsetY = adGame->_sceneViewport->_offsetY;
+ }
+ } else {
+ if (offsetX) {
+ *offsetX = 0;
+ }
+ if (offsetY) {
+ *offsetY = 0;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::getViewportSize(int *width, int *height) {
+ AdGame *adGame = (AdGame *)_gameRef;
+ if (_viewport && !_gameRef->_editorMode) {
+ if (width) {
+ *width = _viewport->getWidth();
+ }
+ if (height) {
+ *height = _viewport->getHeight();
+ }
+ } else if (adGame->_sceneViewport && !_gameRef->_editorMode) {
+ if (width) {
+ *width = adGame->_sceneViewport->getWidth();
+ }
+ if (height) {
+ *height = adGame->_sceneViewport->getHeight();
+ }
+ } else {
+ if (width) {
+ *width = _gameRef->_renderer->_width;
+ }
+ if (height) {
+ *height = _gameRef->_renderer->_height;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int AdScene::getOffsetLeft() {
+ int viewportX;
+ getViewportOffset(&viewportX);
+
+ return _offsetLeft - viewportX;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int AdScene::getOffsetTop() {
+ int viewportY;
+ getViewportOffset(NULL, &viewportY);
+
+ return _offsetTop - viewportY;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::pointInViewport(int x, int y) {
+ int left, top, width, height;
+
+ getViewportOffset(&left, &top);
+ getViewportSize(&width, &height);
+
+ return x >= left && x <= left + width && y >= top && y <= top + height;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdScene::setOffset(int offsetLeft, int offsetTop) {
+ _offsetLeft = offsetLeft;
+ _offsetTop = offsetTop;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseObject *AdScene::getNodeByName(const char *name) {
+ BaseObject *ret = NULL;
+
+ // dependent objects
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ AdLayer *layer = _layers[i];
+ for (uint32 j = 0; j < layer->_nodes.size(); j++) {
+ AdSceneNode *node = layer->_nodes[j];
+ if ((node->_type == OBJECT_ENTITY && !scumm_stricmp(name, node->_entity->getName())) ||
+ (node->_type == OBJECT_REGION && !scumm_stricmp(name, node->_region->getName()))) {
+ switch (node->_type) {
+ case OBJECT_ENTITY:
+ ret = node->_entity;
+ break;
+ case OBJECT_REGION:
+ ret = node->_region;
+ break;
+ default:
+ ret = NULL;
+ }
+ return ret;
+ }
+ }
+ }
+
+ // free entities
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_type == OBJECT_ENTITY && !scumm_stricmp(name, _objects[i]->getName())) {
+ return _objects[i];
+ }
+ }
+
+ // waypoint groups
+ for (uint32 i = 0; i < _waypointGroups.size(); i++) {
+ if (!scumm_stricmp(name, _waypointGroups[i]->getName())) {
+ return _waypointGroups[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::saveState() {
+ return persistState(true);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::loadState() {
+ return persistState(false);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::persistState(bool saving) {
+ if (!_persistentState) {
+ return STATUS_OK;
+ }
+
+ AdGame *adGame = (AdGame *)_gameRef;
+ AdSceneState *state = adGame->getSceneState(getFilename(), saving);
+ if (!state) {
+ return STATUS_OK;
+ }
+
+ AdNodeState *nodeState;
+
+ // dependent objects
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ AdLayer *layer = _layers[i];
+ for (uint32 j = 0; j < layer->_nodes.size(); j++) {
+ AdSceneNode *node = layer->_nodes[j];
+ switch (node->_type) {
+ case OBJECT_ENTITY:
+ if (!node->_entity->_saveState) {
+ continue;
+ }
+ nodeState = state->getNodeState(node->_entity->getName(), saving);
+ if (nodeState) {
+ nodeState->transferEntity(node->_entity, _persistentStateSprites, saving);
+ //if (Saving) NodeState->_active = node->_entity->_active;
+ //else node->_entity->_active = NodeState->_active;
+ }
+ break;
+ case OBJECT_REGION:
+ if (!node->_region->_saveState) {
+ continue;
+ }
+ nodeState = state->getNodeState(node->_region->getName(), saving);
+ if (nodeState) {
+ if (saving) {
+ nodeState->_active = node->_region->_active;
+ } else {
+ node->_region->_active = nodeState->_active;
+ }
+ }
+ break;
+ default:
+ warning("AdScene::PersistState - unhandled enum");
+ break;
+ }
+ }
+ }
+
+ // free entities
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (!_objects[i]->_saveState) {
+ continue;
+ }
+ if (_objects[i]->_type == OBJECT_ENTITY) {
+ nodeState = state->getNodeState(_objects[i]->getName(), saving);
+ if (nodeState) {
+ nodeState->transferEntity((AdEntity *)_objects[i], _persistentStateSprites, saving);
+ //if (Saving) NodeState->_active = _objects[i]->_active;
+ //else _objects[i]->_active = NodeState->_active;
+ }
+ }
+ }
+
+ // waypoint groups
+ for (uint32 i = 0; i < _waypointGroups.size(); i++) {
+ nodeState = state->getNodeState(_waypointGroups[i]->getName(), saving);
+ if (nodeState) {
+ if (saving) {
+ nodeState->_active = _waypointGroups[i]->_active;
+ } else {
+ _waypointGroups[i]->_active = nodeState->_active;
+ }
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float AdScene::getRotationAt(int x, int y) {
+ AdRotLevel *prev = NULL;
+ AdRotLevel *next = NULL;
+
+ for (uint32 i = 0; i < _rotLevels.size(); i++) {
+ /* AdRotLevel *xxx = _rotLevels[i];
+ int j = _rotLevels.size();*/
+ if (_rotLevels[i]->_posX < x) {
+ prev = _rotLevels[i];
+ } else {
+ next = _rotLevels[i];
+ break;
+ }
+ }
+
+ if (prev == NULL || next == NULL) {
+ return 0;
+ }
+
+ int delta_x = next->_posX - prev->_posX;
+ float delta_rot = next->_rotation - prev->_rotation;
+ x -= prev->_posX;
+
+ float percent = (float)x / ((float)delta_x / 100.0f);
+ return prev->_rotation + delta_rot / 100 * percent;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::handleItemAssociations(const char *itemName, bool show) {
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ AdLayer *layer = _layers[i];
+ for (uint32 j = 0; j < layer->_nodes.size(); j++) {
+ if (layer->_nodes[j]->_type == OBJECT_ENTITY) {
+ AdEntity *ent = layer->_nodes[j]->_entity;
+
+ if (ent->_item && strcmp(ent->_item, itemName) == 0) {
+ ent->_active = show;
+ }
+ }
+ }
+ }
+
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ if (_objects[i]->_type == OBJECT_ENTITY) {
+ AdEntity *ent = (AdEntity *)_objects[i];
+ if (ent->_item && strcmp(ent->_item, itemName) == 0) {
+ ent->_active = show;
+ }
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::getRegionsAt(int x, int y, AdRegion **regionList, int numRegions) {
+ int numUsed = 0;
+ if (_mainLayer) {
+ for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) {
+ AdSceneNode *node = _mainLayer->_nodes[i];
+ if (node->_type == OBJECT_REGION && node->_region->_active && node->_region->pointInRegion(x, y)) {
+ if (numUsed < numRegions - 1) {
+ regionList[numUsed] = node->_region;
+ numUsed++;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ for (int i = numUsed; i < numRegions; i++) {
+ regionList[i] = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::restoreDeviceObjects() {
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseObject *AdScene::getNextAccessObject(BaseObject *currObject) {
+ BaseArray<AdObject *> objects;
+ getSceneObjects(objects, true);
+
+ if (objects.size() == 0) {
+ return NULL;
+ } else {
+ if (currObject != NULL) {
+ for (uint32 i = 0; i < objects.size(); i++) {
+ if (objects[i] == currObject) {
+ if (i < objects.size() - 1) {
+ return objects[i + 1];
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ return objects[0];
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseObject *AdScene::getPrevAccessObject(BaseObject *currObject) {
+ BaseArray<AdObject *> objects;
+ getSceneObjects(objects, true);
+
+ if (objects.size() == 0) {
+ return NULL;
+ } else {
+ if (currObject != NULL) {
+ for (int i = objects.size() - 1; i >= 0; i--) {
+ if (objects[i] == currObject) {
+ if (i > 0) {
+ return objects[i - 1];
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ return objects[objects.size() - 1];
+ }
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::getSceneObjects(BaseArray<AdObject *> &objects, bool interactiveOnly) {
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ // close-up layer -> remove everything below it
+ if (interactiveOnly && _layers[i]->_closeUp) {
+ objects.clear();
+ }
+
+
+ for (uint32 j = 0; j < _layers[i]->_nodes.size(); j++) {
+ AdSceneNode *node = _layers[i]->_nodes[j];
+ switch (node->_type) {
+ case OBJECT_ENTITY: {
+ AdEntity *ent = node->_entity;
+ if (ent->_active && (ent->_registrable || !interactiveOnly)) {
+ objects.add(ent);
+ }
+ }
+ break;
+
+ case OBJECT_REGION: {
+ BaseArray<AdObject *> regionObj;
+ getRegionObjects(node->_region, regionObj, interactiveOnly);
+ for (uint32 newIndex = 0; newIndex < regionObj.size(); newIndex++) {
+ bool found = false;
+ for (uint32 old = 0; old < objects.size(); old++) {
+ if (objects[old] == regionObj[newIndex]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ objects.add(regionObj[newIndex]);
+ }
+ }
+ //if (regionObj.size() > 0) Objects.Append(RegionObj);
+ }
+ break;
+ default:
+ debugC(kWintermuteDebugGeneral, "AdScene::GetSceneObjects - Unhandled enum");
+ break;
+ }
+ }
+ }
+
+ // objects outside any region
+ BaseArray<AdObject *> regionObj;
+ getRegionObjects(NULL, regionObj, interactiveOnly);
+ for (uint32 newIndex = 0; newIndex < regionObj.size(); newIndex++) {
+ bool found = false;
+ for (uint32 old = 0; old < objects.size(); old++) {
+ if (objects[old] == regionObj[newIndex]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ objects.add(regionObj[newIndex]);
+ }
+ }
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdScene::getRegionObjects(AdRegion *region, BaseArray<AdObject *> &objects, bool interactiveOnly) {
+ AdGame *adGame = (AdGame *)_gameRef;
+ AdObject *obj;
+
+ // global objects
+ for (uint32 i = 0; i < adGame->_objects.size(); i++) {
+ obj = adGame->_objects[i];
+ if (obj->_active && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) {
+ if (interactiveOnly && !obj->_registrable) {
+ continue;
+ }
+
+ objects.add(obj);
+ }
+ }
+
+ // scene objects
+ for (uint32 i = 0; i < _objects.size(); i++) {
+ obj = _objects[i];
+ if (obj->_active && !obj->_editorOnly && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) {
+ if (interactiveOnly && !obj->_registrable) {
+ continue;
+ }
+
+ objects.add(obj);
+ }
+ }
+
+ // sort by _posY
+ Common::sort(objects.begin(), objects.end(), AdScene::compareObjs);
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_scene.h b/engines/wintermute/ad/ad_scene.h
new file mode 100644
index 0000000000..3b482403b5
--- /dev/null
+++ b/engines/wintermute/ad/ad_scene.h
@@ -0,0 +1,181 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADSCENE_H
+#define WINTERMUTE_ADSCENE_H
+
+#include "engines/wintermute/base/base_fader.h"
+
+namespace Wintermute {
+
+class UIWindow;
+class AdObject;
+class AdRegion;
+class BaseViewport;
+class AdLayer;
+class BasePoint;
+class AdWaypointGroup;
+class AdPath;
+class AdScaleLevel;
+class AdRotLevel;
+class AdPathPoint;
+class AdScene : public BaseObject {
+public:
+
+ BaseObject *getNextAccessObject(BaseObject *currObject);
+ BaseObject *getPrevAccessObject(BaseObject *currObject);
+ bool getSceneObjects(BaseArray<AdObject *> &objects, bool interactiveOnly);
+ bool getRegionObjects(AdRegion *region, BaseArray<AdObject *> &objects, bool interactiveOnly);
+
+ bool afterLoad();
+
+ bool getRegionsAt(int x, int y, AdRegion **regionList, int numRegions);
+ bool handleItemAssociations(const char *itemName, bool show);
+ UIWindow *_shieldWindow;
+ float getRotationAt(int x, int y);
+ bool loadState();
+ bool saveState();
+ bool _persistentState;
+ bool _persistentStateSprites;
+ BaseObject *getNodeByName(const char *name);
+ void setOffset(int offsetLeft, int offsetTop);
+ bool pointInViewport(int x, int y);
+ int getOffsetTop();
+ int getOffsetLeft();
+ bool getViewportSize(int *width = NULL, int *height = NULL);
+ bool getViewportOffset(int *offsetX = NULL, int *offsetY = NULL);
+ BaseViewport *_viewport;
+ BaseFader *_fader;
+ int _pfPointsNum;
+ void pfPointsAdd(int x, int y, int distance);
+ void pfPointsStart();
+ bool _initialized;
+ bool correctTargetPoint(int startX, int startY, int *x, int *y, bool checkFreeObjects = false, BaseObject *requester = NULL);
+ bool correctTargetPoint2(int startX, int startY, int *targetX, int *targetY, bool checkFreeObjects, BaseObject *requester);
+ DECLARE_PERSISTENT(AdScene, BaseObject)
+ bool displayRegionContent(AdRegion *region = NULL, bool display3DOnly = false);
+ bool displayRegionContentOld(AdRegion *region = NULL);
+ static int compareObjs(const void *obj1, const void *obj2);
+
+ bool updateFreeObjects();
+ bool traverseNodes(bool update = false);
+ float getScaleAt(int y);
+ bool sortScaleLevels();
+ bool sortRotLevels();
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ uint32 getAlphaAt(int x, int y, bool colorCheck = false);
+ bool _paralaxScrolling;
+ void skipTo(int offsetX, int offsetY);
+ void setDefaults();
+ void cleanup();
+ void skipToObject(BaseObject *object);
+ void scrollToObject(BaseObject *object);
+ void scrollTo(int offsetX, int offsetY);
+ virtual bool update();
+ bool _autoScroll;
+ int _targetOffsetTop;
+ int _targetOffsetLeft;
+
+ int _scrollPixelsV;
+ uint32 _scrollTimeV;
+ uint32 _lastTimeV;
+
+ int _scrollPixelsH;
+ uint32 _scrollTimeH;
+ uint32 _lastTimeH;
+
+ virtual bool display();
+ uint32 _pfMaxTime;
+ bool initLoop();
+ void pathFinderStep();
+ bool isBlockedAt(int x, int y, bool checkFreeObjects = false, BaseObject *requester = NULL);
+ bool isWalkableAt(int x, int y, bool checkFreeObjects = false, BaseObject *requester = NULL);
+ AdLayer *_mainLayer;
+ float getZoomAt(int x, int y);
+ bool getPath(BasePoint source, BasePoint target, AdPath *path, BaseObject *requester = NULL);
+ AdScene(BaseGame *inGame);
+ virtual ~AdScene();
+ BaseArray<AdLayer *> _layers;
+ BaseArray<AdObject *> _objects;
+ BaseArray<AdWaypointGroup *> _waypointGroups;
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ int _width;
+ int _height;
+ bool addObject(AdObject *Object);
+ bool removeObject(AdObject *Object);
+ int _editorMarginH;
+ int _editorMarginV;
+ uint32 _editorColFrame;
+ uint32 _editorColEntity;
+ uint32 _editorColRegion;
+ uint32 _editorColBlocked;
+ uint32 _editorColWaypoints;
+ uint32 _editorColEntitySel;
+ uint32 _editorColRegionSel;
+ uint32 _editorColBlockedSel;
+ uint32 _editorColWaypointsSel;
+ uint32 _editorColScale;
+ uint32 _editorColDecor;
+ uint32 _editorColDecorSel;
+
+ bool _editorShowRegions;
+ bool _editorShowBlocked;
+ bool _editorShowDecor;
+ bool _editorShowEntities;
+ bool _editorShowScale;
+ BaseArray<AdScaleLevel *> _scaleLevels;
+ BaseArray<AdRotLevel *> _rotLevels;
+
+ virtual bool restoreDeviceObjects();
+ int getPointsDist(BasePoint p1, BasePoint p2, BaseObject *requester = NULL);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+
+
+private:
+ bool persistState(bool saving = true);
+ void pfAddWaypointGroup(AdWaypointGroup *Wpt, BaseObject *requester = NULL);
+ bool _pfReady;
+ BasePoint *_pfTarget;
+ AdPath *_pfTargetPath;
+ BaseObject *_pfRequester;
+ BaseArray<AdPathPoint *> _pfPath;
+
+ int _offsetTop;
+ int _offsetLeft;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_scene_node.cpp b/engines/wintermute/ad/ad_scene_node.cpp
new file mode 100644
index 0000000000..d0202236fd
--- /dev/null
+++ b/engines/wintermute/ad/ad_scene_node.cpp
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_scene_node.h"
+#include "engines/wintermute/base/base_game.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdSceneNode, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdSceneNode::AdSceneNode(BaseGame *inGame) : BaseObject(inGame) {
+ _type = OBJECT_NONE;
+ _region = NULL;
+ _entity = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdSceneNode::~AdSceneNode() {
+ _gameRef->unregisterObject(_region);
+ _region = NULL;
+
+ _gameRef->unregisterObject(_entity);
+ _entity = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSceneNode::setEntity(AdEntity *entity) {
+ _type = OBJECT_ENTITY;
+ _entity = entity;
+ return _gameRef->registerObject(entity);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSceneNode::setRegion(AdRegion *region) {
+ _type = OBJECT_REGION;
+ _region = region;
+ return _gameRef->registerObject(region);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSceneNode::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_entity));
+ persistMgr->transfer(TMEMBER(_region));
+ persistMgr->transfer(TMEMBER_INT(_type));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_scene_node.h b/engines/wintermute/ad/ad_scene_node.h
new file mode 100644
index 0000000000..5bb1606d0e
--- /dev/null
+++ b/engines/wintermute/ad/ad_scene_node.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADSCENENODE_H
+#define WINTERMUTE_ADSCENENODE_H
+
+
+#include "engines/wintermute/ad/ad_types.h" // Added by ClassView
+#include "engines/wintermute/ad/ad_region.h" // Added by ClassView
+#include "engines/wintermute/ad/ad_entity.h"
+
+namespace Wintermute {
+
+class AdSceneNode : public BaseObject {
+public:
+ DECLARE_PERSISTENT(AdSceneNode, BaseObject)
+ bool setRegion(AdRegion *region);
+ bool setEntity(AdEntity *entity);
+ AdEntity *_entity;
+ AdRegion *_region;
+ TObjectType _type;
+ AdSceneNode(BaseGame *inGame);
+ virtual ~AdSceneNode();
+
+};
+
+}
+
+#endif
diff --git a/engines/wintermute/ad/ad_scene_state.cpp b/engines/wintermute/ad/ad_scene_state.cpp
new file mode 100644
index 0000000000..6b34f1af53
--- /dev/null
+++ b/engines/wintermute/ad/ad_scene_state.cpp
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_scene_state.h"
+#include "engines/wintermute/ad/ad_node_state.h"
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdSceneState, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdSceneState::AdSceneState(BaseGame *inGame) : BaseClass(inGame) {
+ _filename = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdSceneState::~AdSceneState() {
+ delete[] _filename;
+ _filename = NULL;
+
+ for (uint32 i = 0; i < _nodeStates.size(); i++) {
+ delete _nodeStates[i];
+ }
+ _nodeStates.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSceneState::persist(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_filename));
+ _nodeStates.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdSceneState::setFilename(const char *filename) {
+ delete[] _filename;
+ _filename = new char [strlen(filename) + 1];
+ if (_filename) {
+ strcpy(_filename, filename);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdNodeState *AdSceneState::getNodeState(const char *name, bool saving) {
+ for (uint32 i = 0; i < _nodeStates.size(); i++) {
+ if (scumm_stricmp(_nodeStates[i]->getName(), name) == 0) {
+ return _nodeStates[i];
+ }
+ }
+
+ if (saving) {
+ AdNodeState *ret = new AdNodeState(_gameRef);
+ ret->setName(name);
+ _nodeStates.add(ret);
+
+ return ret;
+ } else {
+ return NULL;
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_scene_state.h b/engines/wintermute/ad/ad_scene_state.h
new file mode 100644
index 0000000000..2b25393c5a
--- /dev/null
+++ b/engines/wintermute/ad/ad_scene_state.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADSCENESTATE_H
+#define WINTERMUTE_ADSCENESTATE_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/coll_templ.h"
+
+namespace Wintermute {
+class AdNodeState;
+class AdSceneState : public BaseClass {
+public:
+ AdNodeState *getNodeState(const char *name, bool saving);
+ void setFilename(const char *filename);
+ DECLARE_PERSISTENT(AdSceneState, BaseClass)
+ AdSceneState(BaseGame *inGame);
+ virtual ~AdSceneState();
+ char *_filename;
+ BaseArray<AdNodeState *> _nodeStates;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_sentence.cpp b/engines/wintermute/ad/ad_sentence.cpp
new file mode 100644
index 0000000000..cfe4191b07
--- /dev/null
+++ b/engines/wintermute/ad/ad_sentence.cpp
@@ -0,0 +1,361 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/ad/ad_sentence.h"
+#include "engines/wintermute/ad/ad_talk_def.h"
+#include "engines/wintermute/ad/ad_talk_node.h"
+#include "engines/wintermute/utils/path_util.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdSentence, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdSentence::AdSentence(BaseGame *inGame) : BaseClass(inGame) {
+ _text = NULL;
+ _stances = NULL;
+ _tempStance = NULL;
+
+ _duration = 0;
+ _startTime = 0;
+ _currentStance = 0;
+
+ _font = NULL;
+
+ _pos.x = _pos.y = 0;
+ _width = _gameRef->_renderer->_width;
+
+ _align = (TTextAlign)TAL_CENTER;
+
+ _sound = NULL;
+ _soundStarted = false;
+
+ _talkDef = NULL;
+ _currentSprite = NULL;
+ _currentSkelAnim = NULL;
+ _fixedPos = false;
+ _freezable = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdSentence::~AdSentence() {
+ delete _sound;
+ delete[] _text;
+ delete[] _stances;
+ delete[] _tempStance;
+ delete _talkDef;
+ _sound = NULL;
+ _text = NULL;
+ _stances = NULL;
+ _tempStance = NULL;
+ _talkDef = NULL;
+
+ _currentSprite = NULL; // ref only
+ _currentSkelAnim = NULL;
+ _font = NULL; // ref only
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdSentence::setText(const char *text) {
+ if (_text) {
+ delete[] _text;
+ }
+ _text = new char[strlen(text) + 1];
+ if (_text) {
+ strcpy(_text, text);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdSentence::setStances(const char *stances) {
+ if (_stances) {
+ delete[] _stances;
+ }
+ if (stances) {
+ _stances = new char[strlen(stances) + 1];
+ if (_stances) {
+ strcpy(_stances, stances);
+ }
+ } else {
+ _stances = NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *AdSentence::getCurrentStance() {
+ return getStance(_currentStance);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *AdSentence::getNextStance() {
+ _currentStance++;
+ return getStance(_currentStance);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *AdSentence::getStance(int stance) {
+ if (_stances == NULL) {
+ return NULL;
+ }
+
+ if (_tempStance) {
+ delete[] _tempStance;
+ }
+ _tempStance = NULL;
+
+ char *start;
+ char *curr;
+ int pos;
+
+ if (stance == 0) {
+ start = _stances;
+ } else {
+ pos = 0;
+ start = NULL;
+ curr = _stances;
+ while (pos < stance) {
+ if (*curr == '\0') {
+ break;
+ }
+ if (*curr == ',') {
+ pos++;
+ }
+ curr++;
+ }
+ if (pos == stance) {
+ start = curr;
+ }
+ }
+
+ if (start == NULL) {
+ return NULL;
+ }
+
+ while (*start == ' ' && *start != ',' && *start != '\0') {
+ start++;
+ }
+
+ curr = start;
+ while (*curr != '\0' && *curr != ',') {
+ curr++;
+ }
+
+ while (curr > start && *(curr - 1) == ' ') {
+ curr--;
+ }
+
+ _tempStance = new char [curr - start + 1];
+ if (_tempStance) {
+ Common::strlcpy(_tempStance, start, curr - start + 1);
+ }
+
+ return _tempStance;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSentence::display() {
+ if (!_font || !_text) {
+ return STATUS_FAILED;
+ }
+
+ if (_sound && !_soundStarted) {
+ _sound->play();
+ _soundStarted = true;
+ }
+
+ if (_gameRef->_subtitles) {
+ int x = _pos.x;
+ int y = _pos.y;
+
+ if (!_fixedPos) {
+ x = x - ((AdGame *)_gameRef)->_scene->getOffsetLeft();
+ y = y - ((AdGame *)_gameRef)->_scene->getOffsetTop();
+ }
+
+
+ x = MAX(x, 0);
+ x = MIN(x, _gameRef->_renderer->_width - _width);
+ y = MAX(y, 0);
+
+ _font->drawText((byte *)_text, x, y, _width, _align);
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdSentence::setSound(BaseSound *sound) {
+ if (!sound) {
+ return;
+ }
+ delete _sound;
+ _sound = sound;
+ _soundStarted = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSentence::finish() {
+ if (_sound) {
+ _sound->stop();
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSentence::persist(BasePersistenceManager *persistMgr) {
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER_INT(_align));
+ persistMgr->transfer(TMEMBER(_currentStance));
+ persistMgr->transfer(TMEMBER(_currentSprite));
+ persistMgr->transfer(TMEMBER(_currentSkelAnim));
+ persistMgr->transfer(TMEMBER(_duration));
+ persistMgr->transfer(TMEMBER(_font));
+ persistMgr->transfer(TMEMBER(_pos));
+ persistMgr->transfer(TMEMBER(_sound));
+ persistMgr->transfer(TMEMBER(_soundStarted));
+ persistMgr->transfer(TMEMBER(_stances));
+ persistMgr->transfer(TMEMBER(_startTime));
+ persistMgr->transfer(TMEMBER(_talkDef));
+ persistMgr->transfer(TMEMBER(_tempStance));
+ persistMgr->transfer(TMEMBER(_text));
+ persistMgr->transfer(TMEMBER(_width));
+ persistMgr->transfer(TMEMBER(_fixedPos));
+ persistMgr->transfer(TMEMBER(_freezable));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSentence::setupTalkFile(const char *soundFilename) {
+ delete _talkDef;
+ _talkDef = NULL;
+ _currentSprite = NULL;
+
+ if (!soundFilename) {
+ return STATUS_OK;
+ }
+
+
+ AnsiString path = PathUtil::getDirectoryName(soundFilename);
+ AnsiString name = PathUtil::getFileNameWithoutExtension(soundFilename);
+
+ AnsiString talkDefFileName = PathUtil::combine(path, name + ".talk");
+
+ if (!BaseFileManager::getEngineInstance()->hasFile(talkDefFileName)) {
+ return STATUS_OK; // no talk def file found
+ }
+
+ _talkDef = new AdTalkDef(_gameRef);
+ if (!_talkDef || DID_FAIL(_talkDef->loadFile(talkDefFileName.c_str()))) {
+ delete _talkDef;
+ _talkDef = NULL;
+ return STATUS_FAILED;
+ }
+ //_gameRef->LOG(0, "Using .talk file: %s", TalkDefFile);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSentence::update(TDirection dir) {
+ if (!_talkDef) {
+ return STATUS_OK;
+ }
+
+ uint32 currentTime;
+ // if sound is available, synchronize with sound, otherwise use timer
+
+ /*
+ if (_sound) CurrentTime = _sound->GetPositionTime();
+ else CurrentTime = _gameRef->_timer - _startTime;
+ */
+ currentTime = _gameRef->_timer - _startTime;
+
+ bool talkNodeFound = false;
+ for (uint32 i = 0; i < _talkDef->_nodes.size(); i++) {
+ if (_talkDef->_nodes[i]->isInTimeInterval(currentTime, dir)) {
+ talkNodeFound = true;
+
+ BaseSprite *newSprite = _talkDef->_nodes[i]->getSprite(dir);
+ if (newSprite != _currentSprite) {
+ newSprite->reset();
+ }
+ _currentSprite = newSprite;
+
+ if (!_talkDef->_nodes[i]->_playToEnd) {
+ break;
+ }
+ }
+ }
+
+
+ // no talk node, try to use default sprite instead (if any)
+ if (!talkNodeFound) {
+ BaseSprite *newSprite = _talkDef->getDefaultSprite(dir);
+ if (newSprite) {
+ if (newSprite != _currentSprite) {
+ newSprite->reset();
+ }
+ _currentSprite = newSprite;
+ } else {
+ _currentSprite = NULL;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSentence::canSkip() {
+ // prevent accidental sentence skipping (TODO make configurable)
+ return (_gameRef->_timer - _startTime) > 300;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_sentence.h b/engines/wintermute/ad/ad_sentence.h
new file mode 100644
index 0000000000..e7c94030b9
--- /dev/null
+++ b/engines/wintermute/ad/ad_sentence.h
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADSENTENCE_H
+#define WINTERMUTE_ADSENTENCE_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/math/rect32.h"
+#include "engines/wintermute/dctypes.h" // Added by ClassView
+#include "common/rect.h"
+
+namespace Wintermute {
+class AdTalkDef;
+class BaseFont;
+class BaseSprite;
+class BaseSound;
+class AdSentence : public BaseClass {
+public:
+ bool _freezable;
+ bool _fixedPos;
+ BaseSprite *_currentSprite;
+ char *_currentSkelAnim;
+ bool update(TDirection dir = DI_DOWN);
+ bool setupTalkFile(const char *soundFilename);
+ DECLARE_PERSISTENT(AdSentence, BaseClass)
+ bool finish();
+ void setSound(BaseSound *Sound);
+ bool _soundStarted;
+ BaseSound *_sound;
+ TTextAlign _align;
+ bool display();
+ int _width;
+ Point32 _pos;
+ BaseFont *_font;
+ char *getNextStance();
+ char *getCurrentStance();
+ void setStances(const char *stances);
+ void setText(const char *text);
+ int _currentStance;
+ uint32 _startTime;
+ char *_stances;
+ char *_text;
+ uint32 _duration;
+ AdSentence(BaseGame *inGame);
+ virtual ~AdSentence();
+ AdTalkDef *_talkDef;
+
+ bool canSkip();
+
+private:
+ char *_tempStance;
+ char *getStance(int stance);
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_sprite_set.cpp b/engines/wintermute/ad/ad_sprite_set.cpp
new file mode 100644
index 0000000000..345b483a8f
--- /dev/null
+++ b/engines/wintermute/ad/ad_sprite_set.cpp
@@ -0,0 +1,356 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_sprite_set.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_sprite.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdSpriteSet, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdSpriteSet::AdSpriteSet(BaseGame *inGame, BaseObject *owner) : BaseObject(inGame) {
+ _owner = owner;
+
+ for (int i = 0; i < NUM_DIRECTIONS; i++) {
+ _sprites[i] = NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdSpriteSet::~AdSpriteSet() {
+ for (int i = 0; i < NUM_DIRECTIONS; i++) {
+ delete _sprites[i];
+ _sprites[i] = NULL;
+ }
+
+ _owner = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSpriteSet::loadFile(const char *filename, int lifeTime, TSpriteCacheType cacheType) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdSpriteSet::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing SPRITESET file '%s'", filename);
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(SPRITESET)
+TOKEN_DEF(NAME)
+TOKEN_DEF(UP_LEFT)
+TOKEN_DEF(DOWN_LEFT)
+TOKEN_DEF(LEFT)
+TOKEN_DEF(UP_RIGHT)
+TOKEN_DEF(DOWN_RIGHT)
+TOKEN_DEF(RIGHT)
+TOKEN_DEF(UP)
+TOKEN_DEF(DOWN)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdSpriteSet::loadBuffer(byte *buffer, bool complete, int lifeTime, TSpriteCacheType cacheType) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(SPRITESET)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(UP_LEFT)
+ TOKEN_TABLE(DOWN_LEFT)
+ TOKEN_TABLE(LEFT)
+ TOKEN_TABLE(UP_RIGHT)
+ TOKEN_TABLE(DOWN_RIGHT)
+ TOKEN_TABLE(RIGHT)
+ TOKEN_TABLE(UP)
+ TOKEN_TABLE(DOWN)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_SPRITESET) {
+ _gameRef->LOG(0, "'SPRITESET' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ BaseSprite *spr = NULL;
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_LEFT:
+ delete _sprites[DI_LEFT];
+ _sprites[DI_LEFT] = NULL;
+ spr = new BaseSprite(_gameRef, _owner);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprites[DI_LEFT] = spr;
+ }
+ break;
+
+ case TOKEN_RIGHT:
+ delete _sprites[DI_RIGHT];
+ _sprites[DI_RIGHT] = NULL;
+ spr = new BaseSprite(_gameRef, _owner);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprites[DI_RIGHT] = spr;
+ }
+ break;
+
+ case TOKEN_UP:
+ delete _sprites[DI_UP];
+ _sprites[DI_UP] = NULL;
+ spr = new BaseSprite(_gameRef, _owner);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprites[DI_UP] = spr;
+ }
+ break;
+
+ case TOKEN_DOWN:
+ delete _sprites[DI_DOWN];
+ _sprites[DI_DOWN] = NULL;
+ spr = new BaseSprite(_gameRef, _owner);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprites[DI_DOWN] = spr;
+ }
+ break;
+
+ case TOKEN_UP_LEFT:
+ delete _sprites[DI_UPLEFT];
+ _sprites[DI_UPLEFT] = NULL;
+ spr = new BaseSprite(_gameRef, _owner);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprites[DI_UPLEFT] = spr;
+ }
+ break;
+
+ case TOKEN_UP_RIGHT:
+ delete _sprites[DI_UPRIGHT];
+ _sprites[DI_UPRIGHT] = NULL;
+ spr = new BaseSprite(_gameRef, _owner);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprites[DI_UPRIGHT] = spr;
+ }
+ break;
+
+ case TOKEN_DOWN_LEFT:
+ delete _sprites[DI_DOWNLEFT];
+ _sprites[DI_DOWNLEFT] = NULL;
+ spr = new BaseSprite(_gameRef, _owner);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprites[DI_DOWNLEFT] = spr;
+ }
+ break;
+
+ case TOKEN_DOWN_RIGHT:
+ delete _sprites[DI_DOWNRIGHT];
+ _sprites[DI_DOWNRIGHT] = NULL;
+ spr = new BaseSprite(_gameRef, _owner);
+ if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) {
+ cmd = PARSERR_GENERIC;
+ } else {
+ _sprites[DI_DOWNRIGHT] = spr;
+ }
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in SPRITESET definition");
+ return STATUS_FAILED;
+ }
+
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading SPRITESET definition");
+ if (spr) {
+ delete spr;
+ }
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSpriteSet::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_owner));
+ for (int i = 0; i < NUM_DIRECTIONS; i++) {
+ persistMgr->transfer("", &_sprites[i]);
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSprite *AdSpriteSet::getSprite(TDirection direction) {
+ int dir = (int)direction;
+ if (dir < 0) {
+ dir = 0;
+ }
+ if (dir >= NUM_DIRECTIONS) {
+ dir = NUM_DIRECTIONS - 1;
+ }
+
+ BaseSprite *ret = NULL;
+
+ // find nearest set sprite
+ int numSteps = 0;
+ for (int i = dir; i >= 0; i--) {
+ if (_sprites[i] != NULL) {
+ ret = _sprites[i];
+ numSteps = dir - i;
+ break;
+ }
+ }
+
+ for (int i = dir; i < NUM_DIRECTIONS; i++) {
+ if (_sprites[i] != NULL) {
+ if (ret == NULL || numSteps > i - dir) {
+ return _sprites[i];
+ } else {
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSpriteSet::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "SPRITESET {\n");
+ if (getName()) {
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ }
+ for (int i = 0; i < NUM_DIRECTIONS; i++) {
+ if (_sprites[i]) {
+ switch (i) {
+ case DI_UP:
+ buffer->putTextIndent(indent + 2, "UP=\"%s\"\n", _sprites[i]->getFilename());
+ break;
+ case DI_UPRIGHT:
+ buffer->putTextIndent(indent + 2, "UP_RIGHT=\"%s\"\n", _sprites[i]->getFilename());
+ break;
+ case DI_RIGHT:
+ buffer->putTextIndent(indent + 2, "RIGHT=\"%s\"\n", _sprites[i]->getFilename());
+ break;
+ case DI_DOWNRIGHT:
+ buffer->putTextIndent(indent + 2, "DOWN_RIGHT=\"%s\"\n", _sprites[i]->getFilename());
+ break;
+ case DI_DOWN:
+ buffer->putTextIndent(indent + 2, "DOWN=\"%s\"\n", _sprites[i]->getFilename());
+ break;
+ case DI_DOWNLEFT:
+ buffer->putTextIndent(indent + 2, "DOWN_LEFT=\"%s\"\n", _sprites[i]->getFilename());
+ break;
+ case DI_LEFT:
+ buffer->putTextIndent(indent + 2, "LEFT=\"%s\"\n", _sprites[i]->getFilename());
+ break;
+ case DI_UPLEFT:
+ buffer->putTextIndent(indent + 2, "UP_LEFT=\"%s\"\n", _sprites[i]->getFilename());
+ break;
+ }
+ }
+ }
+
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool AdSpriteSet::containsSprite(BaseSprite *sprite) {
+ if (!sprite) {
+ return false;
+ }
+
+ for (int i = 0; i < NUM_DIRECTIONS; i++) {
+ if (_sprites[i] == sprite) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_sprite_set.h b/engines/wintermute/ad/ad_sprite_set.h
new file mode 100644
index 0000000000..ba5da0ff2e
--- /dev/null
+++ b/engines/wintermute/ad/ad_sprite_set.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADSPRITESET_H
+#define WINTERMUTE_ADSPRITESET_H
+
+
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+class BaseSprite;
+class AdSpriteSet : public BaseObject {
+public:
+ bool containsSprite(BaseSprite *sprite);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0);
+ BaseSprite *getSprite(TDirection direction);
+ DECLARE_PERSISTENT(AdSpriteSet, BaseObject)
+ BaseObject *_owner;
+ AdSpriteSet(BaseGame *inGame, BaseObject *owner = NULL);
+ virtual ~AdSpriteSet();
+ bool loadFile(const char *filename, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL);
+ bool loadBuffer(byte *buffer, bool complete = true, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL);
+ BaseSprite *_sprites[NUM_DIRECTIONS];
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_talk_def.cpp b/engines/wintermute/ad/ad_talk_def.cpp
new file mode 100644
index 0000000000..a85cd7f986
--- /dev/null
+++ b/engines/wintermute/ad/ad_talk_def.cpp
@@ -0,0 +1,285 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_sprite_set.h"
+#include "engines/wintermute/ad/ad_talk_def.h"
+#include "engines/wintermute/ad/ad_talk_node.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/utils/utils.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdTalkDef, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdTalkDef::AdTalkDef(BaseGame *inGame) : BaseObject(inGame) {
+ _defaultSpriteFilename = NULL;
+ _defaultSprite = NULL;
+
+ _defaultSpriteSetFilename = NULL;
+ _defaultSpriteSet = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdTalkDef::~AdTalkDef() {
+ for (uint32 i = 0; i < _nodes.size(); i++) {
+ delete _nodes[i];
+ }
+ _nodes.clear();
+
+ delete[] _defaultSpriteFilename;
+ delete _defaultSprite;
+ _defaultSpriteFilename = NULL;
+ _defaultSprite = NULL;
+
+ delete[] _defaultSpriteSetFilename;
+ delete _defaultSpriteSet;
+ _defaultSpriteSetFilename = NULL;
+ _defaultSpriteSet = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkDef::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdTalkDef::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing TALK file '%s'", filename);
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(TALK)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(ACTION)
+TOKEN_DEF(DEFAULT_SPRITESET_FILE)
+TOKEN_DEF(DEFAULT_SPRITESET)
+TOKEN_DEF(DEFAULT_SPRITE)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkDef::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(TALK)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(ACTION)
+ TOKEN_TABLE(DEFAULT_SPRITESET_FILE)
+ TOKEN_TABLE(DEFAULT_SPRITESET)
+ TOKEN_TABLE(DEFAULT_SPRITE)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_TALK) {
+ _gameRef->LOG(0, "'TALK' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_ACTION: {
+ AdTalkNode *node = new AdTalkNode(_gameRef);
+ if (node && DID_SUCCEED(node->loadBuffer(params, false))) {
+ _nodes.add(node);
+ } else {
+ delete node;
+ node = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ }
+ break;
+
+ case TOKEN_DEFAULT_SPRITE:
+ BaseUtils::setString(&_defaultSpriteFilename, (char *)params);
+ break;
+
+ case TOKEN_DEFAULT_SPRITESET_FILE:
+ BaseUtils::setString(&_defaultSpriteSetFilename, (char *)params);
+ break;
+
+ case TOKEN_DEFAULT_SPRITESET: {
+ delete _defaultSpriteSet;
+ _defaultSpriteSet = new AdSpriteSet(_gameRef);
+ if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadBuffer(params, false))) {
+ delete _defaultSpriteSet;
+ _defaultSpriteSet = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ }
+ break;
+
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in TALK definition");
+ return STATUS_FAILED;
+ }
+
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading TALK definition");
+ return STATUS_FAILED;
+ }
+
+ delete _defaultSprite;
+ delete _defaultSpriteSet;
+ _defaultSprite = NULL;
+ _defaultSpriteSet = NULL;
+
+ if (_defaultSpriteFilename) {
+ _defaultSprite = new BaseSprite(_gameRef);
+ if (!_defaultSprite || DID_FAIL(_defaultSprite->loadFile(_defaultSpriteFilename))) {
+ return STATUS_FAILED;
+ }
+ }
+
+ if (_defaultSpriteSetFilename) {
+ _defaultSpriteSet = new AdSpriteSet(_gameRef);
+ if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadFile(_defaultSpriteSetFilename))) {
+ return STATUS_FAILED;
+ }
+ }
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkDef::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_defaultSprite));
+ persistMgr->transfer(TMEMBER(_defaultSpriteFilename));
+ persistMgr->transfer(TMEMBER(_defaultSpriteSet));
+ persistMgr->transfer(TMEMBER(_defaultSpriteSetFilename));
+
+ _nodes.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkDef::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "TALK {\n");
+ if (_defaultSpriteFilename) {
+ buffer->putTextIndent(indent + 2, "DEFAULT_SPRITE=\"%s\"\n", _defaultSpriteFilename);
+ }
+
+ if (_defaultSpriteSetFilename) {
+ buffer->putTextIndent(indent + 2, "DEFAULT_SPRITESET_FILE=\"%s\"\n", _defaultSpriteSetFilename);
+ } else if (_defaultSpriteSet) {
+ _defaultSpriteSet->saveAsText(buffer, indent + 2);
+ }
+
+ for (uint32 i = 0; i < _nodes.size(); i++) {
+ _nodes[i]->saveAsText(buffer, indent + 2);
+ buffer->putTextIndent(indent, "\n");
+ }
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkDef::loadDefaultSprite() {
+ if (_defaultSpriteFilename && !_defaultSprite) {
+ _defaultSprite = new BaseSprite(_gameRef);
+ if (!_defaultSprite || DID_FAIL(_defaultSprite->loadFile(_defaultSpriteFilename))) {
+ delete _defaultSprite;
+ _defaultSprite = NULL;
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+ } else if (_defaultSpriteSetFilename && !_defaultSpriteSet) {
+ _defaultSpriteSet = new AdSpriteSet(_gameRef);
+ if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadFile(_defaultSpriteSetFilename))) {
+ delete _defaultSpriteSet;
+ _defaultSpriteSet = NULL;
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSprite *AdTalkDef::getDefaultSprite(TDirection dir) {
+ loadDefaultSprite();
+ if (_defaultSprite) {
+ return _defaultSprite;
+ } else if (_defaultSpriteSet) {
+ return _defaultSpriteSet->getSprite(dir);
+ } else {
+ return NULL;
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_talk_def.h b/engines/wintermute/ad/ad_talk_def.h
new file mode 100644
index 0000000000..d147212775
--- /dev/null
+++ b/engines/wintermute/ad/ad_talk_def.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADTALKDEF_H
+#define WINTERMUTE_ADTALKDEF_H
+
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+class AdTalkNode;
+class AdSpriteSet;
+class AdTalkDef : public BaseObject {
+public:
+ char *_defaultSpriteSetFilename;
+ AdSpriteSet *_defaultSpriteSet;
+ BaseSprite *getDefaultSprite(TDirection Dir);
+ bool loadDefaultSprite();
+ DECLARE_PERSISTENT(AdTalkDef, BaseObject)
+
+ AdTalkDef(BaseGame *inGame);
+ virtual ~AdTalkDef();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ BaseArray<AdTalkNode *> _nodes;
+ char *_defaultSpriteFilename;
+ BaseSprite *_defaultSprite;
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_talk_holder.cpp b/engines/wintermute/ad/ad_talk_holder.cpp
new file mode 100644
index 0000000000..cca4fdc2cb
--- /dev/null
+++ b/engines/wintermute/ad/ad_talk_holder.cpp
@@ -0,0 +1,402 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_talk_holder.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdTalkHolder, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdTalkHolder::AdTalkHolder(BaseGame *inGame) : AdObject(inGame) {
+ _sprite = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdTalkHolder::~AdTalkHolder() {
+ delete _sprite;
+ _sprite = NULL;
+
+ for (uint32 i = 0; i < _talkSprites.size(); i++) {
+ delete _talkSprites[i];
+ }
+ _talkSprites.clear();
+
+ for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
+ delete _talkSpritesEx[i];
+ }
+ _talkSpritesEx.clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseSprite *AdTalkHolder::getTalkStance(const char *stance) {
+ BaseSprite *ret = NULL;
+
+
+ // forced stance?
+ if (_forcedTalkAnimName && !_forcedTalkAnimUsed) {
+ _forcedTalkAnimUsed = true;
+ delete _animSprite;
+ _animSprite = new BaseSprite(_gameRef, this);
+ if (_animSprite) {
+ bool res = _animSprite->loadFile(_forcedTalkAnimName);
+ if (DID_FAIL(res)) {
+ _gameRef->LOG(res, "AdTalkHolder::GetTalkStance: error loading talk sprite (object:\"%s\" sprite:\"%s\")", getName(), _forcedTalkAnimName);
+ delete _animSprite;
+ _animSprite = NULL;
+ } else {
+ return _animSprite;
+ }
+ }
+ }
+
+
+ if (stance != NULL) {
+ // search special talk stances
+ for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
+ if (scumm_stricmp(_talkSpritesEx[i]->getName(), stance) == 0) {
+ ret = _talkSpritesEx[i];
+ break;
+ }
+ }
+ if (ret == NULL) {
+ // serach generic talk stances
+ for (uint32 i = 0; i < _talkSprites.size(); i++) {
+ if (scumm_stricmp(_talkSprites[i]->getName(), stance) == 0) {
+ ret = _talkSprites[i];
+ break;
+ }
+ }
+ }
+ }
+
+ // not a valid stance? get a random one
+ if (ret == NULL) {
+ if (_talkSprites.size() < 1) {
+ ret = _sprite;
+ } else {
+ // TODO: remember last
+ int rnd = BaseEngine::instance().randInt(0, _talkSprites.size() - 1);
+ ret = _talkSprites[rnd];
+ }
+ }
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkHolder::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetSprite
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetSprite") == 0) {
+ stack->correctParams(1);
+
+ ScValue *val = stack->pop();
+
+ bool setCurrent = false;
+ if (_currentSprite && _currentSprite == _sprite) {
+ setCurrent = true;
+ }
+
+ delete _sprite;
+ _sprite = NULL;
+
+ if (val->isNULL()) {
+ _sprite = NULL;
+ if (setCurrent) {
+ _currentSprite = NULL;
+ }
+ stack->pushBool(true);
+ } else {
+ const char *filename = val->getString();
+ BaseSprite *spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile(filename))) {
+ script->runtimeError("SetSprite method failed for file '%s'", filename);
+ stack->pushBool(false);
+ } else {
+ _sprite = spr;
+ if (setCurrent) {
+ _currentSprite = _sprite;
+ }
+ stack->pushBool(true);
+ }
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSprite
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSprite") == 0) {
+ stack->correctParams(0);
+
+ if (!_sprite || !_sprite->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_sprite->getFilename());
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSpriteObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSpriteObject") == 0) {
+ stack->correctParams(0);
+
+ if (!_sprite) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_sprite, true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddTalkSprite
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddTalkSprite") == 0) {
+ stack->correctParams(2);
+
+ const char *filename = stack->pop()->getString();
+ bool ex = stack->pop()->getBool();
+
+ BaseSprite *spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile(filename))) {
+ stack->pushBool(false);
+ script->runtimeError("AddTalkSprite method failed for file '%s'", filename);
+ } else {
+ if (ex) {
+ _talkSpritesEx.add(spr);
+ } else {
+ _talkSprites.add(spr);
+ }
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RemoveTalkSprite
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemoveTalkSprite") == 0) {
+ stack->correctParams(2);
+
+ const char *filename = stack->pop()->getString();
+ bool ex = stack->pop()->getBool();
+
+ bool setCurrent = false;
+ bool setTemp2 = false;
+
+ if (ex) {
+ for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
+ if (scumm_stricmp(_talkSpritesEx[i]->getFilename(), filename) == 0) {
+ if (_currentSprite == _talkSpritesEx[i]) {
+ setCurrent = true;
+ }
+ if (_tempSprite2 == _talkSpritesEx[i]) {
+ setTemp2 = true;
+ }
+ delete _talkSpritesEx[i];
+ _talkSpritesEx.remove_at(i);
+ break;
+ }
+ }
+ } else {
+ for (uint32 i = 0; i < _talkSprites.size(); i++) {
+ if (scumm_stricmp(_talkSprites[i]->getFilename(), filename) == 0) {
+ if (_currentSprite == _talkSprites[i]) {
+ setCurrent = true;
+ }
+ if (_tempSprite2 == _talkSprites[i]) {
+ setTemp2 = true;
+ }
+ delete _talkSprites[i];
+ _talkSprites.remove_at(i);
+ break;
+ }
+ }
+
+ }
+
+ stack->pushBool(true);
+ if (setCurrent) {
+ _currentSprite = _sprite;
+ }
+ if (setTemp2) {
+ _tempSprite2 = _sprite;
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetTalkSprite
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetTalkSprite") == 0) {
+ stack->correctParams(2);
+
+ const char *filename = stack->pop()->getString();
+ bool ex = stack->pop()->getBool();
+ bool setCurrent = false;
+ bool setTemp2 = false;
+
+ BaseSprite *spr = new BaseSprite(_gameRef, this);
+ if (!spr || DID_FAIL(spr->loadFile(filename))) {
+ stack->pushBool(false);
+ script->runtimeError("SetTalkSprite method failed for file '%s'", filename);
+ } else {
+
+ // delete current
+ if (ex) {
+ for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
+ if (_talkSpritesEx[i] == _currentSprite) {
+ setCurrent = true;
+ }
+ if (_talkSpritesEx[i] == _tempSprite2) {
+ setTemp2 = true;
+ }
+ delete _talkSpritesEx[i];
+ }
+ _talkSpritesEx.clear();
+ } else {
+ for (uint32 i = 0; i < _talkSprites.size(); i++) {
+ if (_talkSprites[i] == _currentSprite) {
+ setCurrent = true;
+ }
+ if (_talkSprites[i] == _tempSprite2) {
+ setTemp2 = true;
+ }
+ delete _talkSprites[i];
+ }
+ _talkSprites.clear();
+ }
+
+ // set new
+ if (ex) {
+ _talkSpritesEx.add(spr);
+ } else {
+ _talkSprites.add(spr);
+ }
+ stack->pushBool(true);
+
+ if (setCurrent) {
+ _currentSprite = spr;
+ }
+ if (setTemp2) {
+ _tempSprite2 = spr;
+ }
+ }
+ return STATUS_OK;
+ } else {
+ return AdObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdTalkHolder::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("talk-holder");
+ return _scValue;
+ } else {
+ return AdObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkHolder::scSetProperty(const char *name, ScValue *value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Item
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Item")==0){
+ SetItem(value->getString());
+ return STATUS_OK;
+ }
+
+ else*/ return AdObject::scSetProperty(name, value);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *AdTalkHolder::scToString() {
+ return "[talk-holder object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkHolder::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ for (uint32 i = 0; i < _talkSprites.size(); i++) {
+ if (_talkSprites[i]->getFilename()) {
+ buffer->putTextIndent(indent + 2, "TALK=\"%s\"\n", _talkSprites[i]->getFilename());
+ }
+ }
+
+ for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
+ if (_talkSpritesEx[i]->getFilename()) {
+ buffer->putTextIndent(indent + 2, "TALK_SPECIAL=\"%s\"\n", _talkSpritesEx[i]->getFilename());
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkHolder::persist(BasePersistenceManager *persistMgr) {
+ AdObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_sprite));
+ _talkSprites.persist(persistMgr);
+ _talkSpritesEx.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_talk_holder.h b/engines/wintermute/ad/ad_talk_holder.h
new file mode 100644
index 0000000000..d52ebf63c0
--- /dev/null
+++ b/engines/wintermute/ad/ad_talk_holder.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADTALKHOLDER_H
+#define WINTERMUTE_ADTALKHOLDER_H
+
+#include "engines/wintermute/ad/ad_object.h"
+
+namespace Wintermute {
+
+class AdTalkHolder : public AdObject {
+public:
+ DECLARE_PERSISTENT(AdTalkHolder, AdObject)
+ virtual BaseSprite *getTalkStance(const char *stance);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ BaseSprite *_sprite;
+ BaseArray<BaseSprite *> _talkSprites;
+ BaseArray<BaseSprite *> _talkSpritesEx;
+ AdTalkHolder(BaseGame *inGame);
+ virtual ~AdTalkHolder();
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_talk_node.cpp b/engines/wintermute/ad/ad_talk_node.cpp
new file mode 100644
index 0000000000..c909ee27ff
--- /dev/null
+++ b/engines/wintermute/ad/ad_talk_node.cpp
@@ -0,0 +1,295 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_sprite_set.h"
+#include "engines/wintermute/ad/ad_talk_node.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/utils/utils.h"
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdTalkNode, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdTalkNode::AdTalkNode(BaseGame *inGame) : BaseClass(inGame) {
+ _sprite = NULL;
+ _spriteFilename = NULL;
+ _spriteSet = NULL;
+ _spriteSetFilename = NULL;
+ _comment = NULL;
+
+ _startTime = _endTime = 0;
+ _playToEnd = false;
+ _preCache = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdTalkNode::~AdTalkNode() {
+ delete[] _spriteFilename;
+ delete _sprite;
+ delete[] _spriteSetFilename;
+ delete _spriteSet;
+ delete _comment;
+ _spriteFilename = NULL;
+ _sprite = NULL;
+ _spriteSetFilename = NULL;
+ _spriteSet = NULL;
+ _comment = NULL;
+}
+
+
+
+TOKEN_DEF_START
+TOKEN_DEF(ACTION)
+TOKEN_DEF(SPRITESET_FILE)
+TOKEN_DEF(SPRITESET)
+TOKEN_DEF(SPRITE)
+TOKEN_DEF(START_TIME)
+TOKEN_DEF(END_TIME)
+TOKEN_DEF(COMMENT)
+TOKEN_DEF(PRECACHE)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkNode::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ACTION)
+ TOKEN_TABLE(SPRITESET_FILE)
+ TOKEN_TABLE(SPRITESET)
+ TOKEN_TABLE(SPRITE)
+ TOKEN_TABLE(START_TIME)
+ TOKEN_TABLE(END_TIME)
+ TOKEN_TABLE(COMMENT)
+ TOKEN_TABLE(PRECACHE)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_ACTION) {
+ _gameRef->LOG(0, "'ACTION' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ _endTime = 0;
+ _playToEnd = false;
+ _preCache = false;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_SPRITE:
+ BaseUtils::setString(&_spriteFilename, (char *)params);
+ break;
+
+ case TOKEN_SPRITESET_FILE:
+ BaseUtils::setString(&_spriteSetFilename, (char *)params);
+ break;
+
+ case TOKEN_SPRITESET: {
+ delete _spriteSet;
+ _spriteSet = new AdSpriteSet(_gameRef);
+ if (!_spriteSet || DID_FAIL(_spriteSet->loadBuffer(params, false))) {
+ delete _spriteSet;
+ _spriteSet = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ }
+ break;
+
+ case TOKEN_START_TIME:
+ parser.scanStr((char *)params, "%d", &_startTime);
+ break;
+
+ case TOKEN_END_TIME:
+ parser.scanStr((char *)params, "%d", &_endTime);
+ break;
+
+ case TOKEN_PRECACHE:
+ parser.scanStr((char *)params, "%b", &_preCache);
+ break;
+
+ case TOKEN_COMMENT:
+ if (_gameRef->_editorMode) {
+ BaseUtils::setString(&_comment, (char *)params);
+ }
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in ACTION definition");
+ return STATUS_FAILED;
+ }
+
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading ACTION definition");
+ return STATUS_FAILED;
+ }
+
+ if (_endTime == 0) {
+ _playToEnd = true;
+ } else {
+ _playToEnd = false;
+ }
+
+ if (_preCache && _spriteFilename) {
+ delete _sprite;
+ _sprite = new BaseSprite(_gameRef);
+ if (!_sprite || DID_FAIL(_sprite->loadFile(_spriteFilename))) {
+ return STATUS_FAILED;
+ }
+ }
+
+ if (_preCache && _spriteSetFilename) {
+ delete _spriteSet;
+ _spriteSet = new AdSpriteSet(_gameRef);
+ if (!_spriteSet || DID_FAIL(_spriteSet->loadFile(_spriteSetFilename))) {
+ return STATUS_FAILED;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkNode::persist(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_comment));
+ persistMgr->transfer(TMEMBER(_startTime));
+ persistMgr->transfer(TMEMBER(_endTime));
+ persistMgr->transfer(TMEMBER(_playToEnd));
+ persistMgr->transfer(TMEMBER(_sprite));
+ persistMgr->transfer(TMEMBER(_spriteFilename));
+ persistMgr->transfer(TMEMBER(_spriteSet));
+ persistMgr->transfer(TMEMBER(_spriteSetFilename));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkNode::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "ACTION {\n");
+ if (_comment) {
+ buffer->putTextIndent(indent + 2, "COMMENT=\"%s\"\n", _comment);
+ }
+ buffer->putTextIndent(indent + 2, "START_TIME=%d\n", _startTime);
+ if (!_playToEnd) {
+ buffer->putTextIndent(indent + 2, "END_TIME=%d\n", _endTime);
+ }
+ if (_spriteFilename) {
+ buffer->putTextIndent(indent + 2, "SPRITE=\"%s\"\n", _spriteFilename);
+ }
+ if (_spriteSetFilename) {
+ buffer->putTextIndent(indent + 2, "SPRITESET_FILE=\"%s\"\n", _spriteSetFilename);
+ } else if (_spriteSet) {
+ _spriteSet->saveAsText(buffer, indent + 2);
+ }
+ if (_preCache) {
+ buffer->putTextIndent(indent + 2, "PRECACHE=\"%s\"\n", _preCache ? "TRUE" : "FALSE");
+ }
+
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkNode::loadSprite() {
+ if (_spriteFilename && !_sprite) {
+ _sprite = new BaseSprite(_gameRef);
+ if (!_sprite || DID_FAIL(_sprite->loadFile(_spriteFilename))) {
+ delete _sprite;
+ _sprite = NULL;
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+ } else if (_spriteSetFilename && !_spriteSet) {
+ _spriteSet = new AdSpriteSet(_gameRef);
+ if (!_spriteSet || DID_FAIL(_spriteSet->loadFile(_spriteSetFilename))) {
+ delete _spriteSet;
+ _spriteSet = NULL;
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdTalkNode::isInTimeInterval(uint32 time, TDirection dir) {
+ if (time >= _startTime) {
+ if (_playToEnd) {
+ if ((_spriteFilename && _sprite == NULL) || (_sprite && _sprite->isFinished() == false)) {
+ return true;
+ } else if ((_spriteSetFilename && _spriteSet == NULL) || (_spriteSet && _spriteSet->getSprite(dir) && _spriteSet->getSprite(dir)->isFinished() == false)) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return _endTime >= time;
+ }
+ } else {
+ return false;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSprite *AdTalkNode::getSprite(TDirection dir) {
+ loadSprite();
+ if (_sprite) {
+ return _sprite;
+ } else if (_spriteSet) {
+ return _spriteSet->getSprite(dir);
+ } else {
+ return NULL;
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_talk_node.h b/engines/wintermute/ad/ad_talk_node.h
new file mode 100644
index 0000000000..7dfd861f85
--- /dev/null
+++ b/engines/wintermute/ad/ad_talk_node.h
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADTALKNODE_H
+#define WINTERMUTE_ADTALKNODE_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+class AdSpriteSet;
+class BaseSprite;
+class AdTalkNode : public BaseClass {
+public:
+ char *_spriteSetFilename;
+ AdSpriteSet *_spriteSet;
+ BaseSprite *getSprite(TDirection dir);
+ bool isInTimeInterval(uint32 time, TDirection dir);
+ bool loadSprite();
+ DECLARE_PERSISTENT(AdTalkNode, BaseClass)
+
+ AdTalkNode(BaseGame *inGame);
+ virtual ~AdTalkNode();
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0);
+ char *_spriteFilename;
+ BaseSprite *_sprite;
+ uint32 _startTime;
+ uint32 _endTime;
+ bool _playToEnd;
+ bool _preCache;
+ char *_comment;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_types.h b/engines/wintermute/ad/ad_types.h
new file mode 100644
index 0000000000..ae5882f4ee
--- /dev/null
+++ b/engines/wintermute/ad/ad_types.h
@@ -0,0 +1,107 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADTYPES_H
+#define WINTERMUTE_ADTYPES_H
+
+namespace Wintermute {
+
+typedef enum {
+ GAME_NORMAL,
+ GAME_WAITING_RESPONSE
+} TGameStateEx;
+
+typedef enum {
+ OBJECT_ENTITY,
+ OBJECT_REGION,
+ OBJECT_ACTOR,
+ OBJECT_NONE
+} TObjectType;
+
+typedef enum {
+ ENTITY_NORMAL,
+ ENTITY_SOUND
+} TEntityType;
+
+typedef enum {
+ STATE_NONE,
+ STATE_IDLE,
+ STATE_PLAYING_ANIM,
+ STATE_READY,
+ STATE_FOLLOWING_PATH,
+ STATE_SEARCHING_PATH,
+ STATE_WAITING_PATH,
+ STATE_TURNING_LEFT,
+ STATE_TURNING_RIGHT,
+ STATE_TURNING,
+ STATE_TALKING,
+ STATE_DIRECT_CONTROL,
+ STATE_PLAYING_ANIM_SET
+} TObjectState;
+
+typedef enum {
+ DIRECT_WALK_NONE,
+ DIRECT_WALK_FW,
+ DIRECT_WALK_BK
+} TDirectWalkMode;
+
+typedef enum {
+ DIRECT_TURN_NONE,
+ DIRECT_TURN_CW,
+ DIRECT_TURN_CCW
+} TDirectTurnMode;
+
+typedef enum {
+ RESPONSE_TEXT,
+ RESPONSE_ICON
+} TResponseStyle;
+
+typedef enum {
+ RESPONSE_ALWAYS,
+ RESPONSE_ONCE,
+ RESPONSE_ONCE_GAME
+} TResponseType;
+
+
+typedef enum {
+ TALK_SKIP_LEFT = 0,
+ TALK_SKIP_RIGHT = 1,
+ TALK_SKIP_BOTH = 2,
+ TALK_SKIP_NONE = 3
+} TTalkSkipButton;
+
+typedef enum {
+ GEOM_WAYPOINT,
+ GEOM_WALKPLANE,
+ GEOM_BLOCKED,
+ GEOM_GENERIC
+} TGeomNodeType;
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ad/ad_waypoint_group.cpp b/engines/wintermute/ad/ad_waypoint_group.cpp
new file mode 100644
index 0000000000..81493ce769
--- /dev/null
+++ b/engines/wintermute/ad/ad_waypoint_group.cpp
@@ -0,0 +1,270 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_waypoint_group.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include <limits.h>
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(AdWaypointGroup, false)
+
+//////////////////////////////////////////////////////////////////////////
+AdWaypointGroup::AdWaypointGroup(BaseGame *inGame) : BaseObject(inGame) {
+ _active = true;
+ _editorSelectedPoint = -1;
+ _lastMimicScale = -1;
+ _lastMimicX = _lastMimicY = INT_MIN;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+AdWaypointGroup::~AdWaypointGroup() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void AdWaypointGroup::cleanup() {
+ for (uint32 i = 0; i < _points.size(); i++) {
+ delete _points[i];
+ }
+ _points.clear();
+ _editorSelectedPoint = -1;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdWaypointGroup::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "AdWaypointGroup::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing WAYPOINTS file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(WAYPOINTS)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(NAME)
+TOKEN_DEF(POINT)
+TOKEN_DEF(EDITOR_SELECTED_POINT)
+TOKEN_DEF(EDITOR_SELECTED)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool AdWaypointGroup::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(WAYPOINTS)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(POINT)
+ TOKEN_TABLE(EDITOR_SELECTED_POINT)
+ TOKEN_TABLE(EDITOR_SELECTED)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_WAYPOINTS) {
+ _gameRef->LOG(0, "'WAYPOINTS' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_POINT: {
+ int x, y;
+ parser.scanStr((char *)params, "%d,%d", &x, &y);
+ _points.add(new BasePoint(x, y));
+ }
+ break;
+
+ case TOKEN_EDITOR_SELECTED:
+ parser.scanStr((char *)params, "%b", &_editorSelected);
+ break;
+
+ case TOKEN_EDITOR_SELECTED_POINT:
+ parser.scanStr((char *)params, "%d", &_editorSelectedPoint);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in WAYPOINTS definition");
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdWaypointGroup::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "WAYPOINTS {\n");
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "EDITOR_SELECTED_POINT=%d\n", _editorSelectedPoint);
+
+ if (_scProp) {
+ _scProp->saveAsText(buffer, indent + 2);
+ }
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ for (uint32 i = 0; i < _points.size(); i++) {
+ buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y);
+ }
+
+ buffer->putTextIndent(indent, "}\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdWaypointGroup::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_active));
+ persistMgr->transfer(TMEMBER(_editorSelectedPoint));
+ persistMgr->transfer(TMEMBER(_lastMimicScale));
+ persistMgr->transfer(TMEMBER(_lastMimicX));
+ persistMgr->transfer(TMEMBER(_lastMimicY));
+ _points.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *AdWaypointGroup::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("waypoint-group");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Active
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Active") {
+ _scValue->setBool(_active);
+ return _scValue;
+ } else {
+ return BaseObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdWaypointGroup::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Active
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Active") == 0) {
+ _active = value->getBool();
+ return STATUS_OK;
+ }
+
+ else {
+ return BaseObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool AdWaypointGroup::mimic(AdWaypointGroup *wpt, float scale, int argX, int argY) {
+ if (scale == _lastMimicScale && argX == _lastMimicX && argY == _lastMimicY) {
+ return STATUS_OK;
+ }
+
+ cleanup();
+
+ for (uint32 i = 0; i < wpt->_points.size(); i++) {
+ int x = (int)((float)wpt->_points[i]->x * scale / 100.0f);
+ int y = (int)((float)wpt->_points[i]->y * scale / 100.0f);
+
+ _points.add(new BasePoint(x + argX, y + argY));
+ }
+
+ _lastMimicScale = scale;
+ _lastMimicX = argX;
+ _lastMimicY = argY;
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ad/ad_waypoint_group.h b/engines/wintermute/ad/ad_waypoint_group.h
new file mode 100644
index 0000000000..13d6bbadd7
--- /dev/null
+++ b/engines/wintermute/ad/ad_waypoint_group.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_ADWAYPOINTGROUP_H
+#define WINTERMUTE_ADWAYPOINTGROUP_H
+
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+class BasePoint;
+class AdWaypointGroup : public BaseObject {
+public:
+ float _lastMimicScale;
+ int _lastMimicX;
+ int _lastMimicY;
+ void cleanup();
+ bool mimic(AdWaypointGroup *wpt, float scale = 100.0f, int x = 0, int y = 0);
+ DECLARE_PERSISTENT(AdWaypointGroup, BaseObject)
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ bool _active;
+ AdWaypointGroup(BaseGame *inGame);
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual ~AdWaypointGroup();
+ BaseArray<BasePoint *> _points;
+ int _editorSelectedPoint;
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base.cpp b/engines/wintermute/base/base.cpp
new file mode 100644
index 0000000000..e351792e61
--- /dev/null
+++ b/engines/wintermute/base/base.cpp
@@ -0,0 +1,185 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+BaseClass::BaseClass(BaseGame *gameOwner) {
+ _gameRef = gameOwner;
+ _persistable = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseClass::BaseClass() {
+ _gameRef = NULL;
+ _persistable = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseClass::~BaseClass() {
+ _editorProps.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+Common::String BaseClass::getEditorProp(const Common::String &propName, const Common::String &initVal) {
+ _editorPropsIter = _editorProps.find(propName);
+ if (_editorPropsIter != _editorProps.end()) {
+ return _editorPropsIter->_value.c_str();
+ } else {
+ return initVal;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseClass::setEditorProp(const Common::String &propName, const Common::String &propValue) {
+ if (propName.size() == 0) {
+ return STATUS_FAILED;
+ }
+
+ if (propValue.size() == 0) {
+ _editorProps.erase(propName);
+ } else {
+ _editorProps[propName] = propValue;
+ }
+ return STATUS_OK;
+}
+
+
+
+TOKEN_DEF_START
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(NAME)
+TOKEN_DEF(VALUE)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool BaseClass::parseEditorProperty(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(VALUE)
+ TOKEN_TABLE_END
+
+
+ if (!_gameRef->_editorMode) {
+ return STATUS_OK;
+ }
+
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_EDITOR_PROPERTY) {
+ _gameRef->LOG(0, "'EDITOR_PROPERTY' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ char *propName = NULL;
+ char *propValue = NULL;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_NAME:
+ delete[] propName;
+ propName = new char[strlen((char *)params) + 1];
+ if (propName) {
+ strcpy(propName, (char *)params);
+ } else {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_VALUE:
+ delete[] propValue;
+ propValue = new char[strlen((char *)params) + 1];
+ if (propValue) {
+ strcpy(propValue, (char *)params);
+ } else {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+ }
+
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ delete[] propName;
+ delete[] propValue;
+ propName = NULL;
+ propValue = NULL;
+ _gameRef->LOG(0, "Syntax error in EDITOR_PROPERTY definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC || propName == NULL || propValue == NULL) {
+ delete[] propName;
+ delete[] propValue;
+ propName = NULL;
+ propValue = NULL;
+ _gameRef->LOG(0, "Error loading EDITOR_PROPERTY definition");
+ return STATUS_FAILED;
+ }
+
+
+ setEditorProp(propName, propValue);
+
+ delete[] propName;
+ delete[] propValue;
+ propName = NULL;
+ propValue = NULL;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseClass::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ _editorPropsIter = _editorProps.begin();
+ while (_editorPropsIter != _editorProps.end()) {
+ buffer->putTextIndent(indent, "EDITOR_PROPERTY\n");
+ buffer->putTextIndent(indent, "{\n");
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", _editorPropsIter->_key.c_str());
+ buffer->putTextIndent(indent + 2, "VALUE=\"%s\"\n", _editorPropsIter->_value.c_str());
+ buffer->putTextIndent(indent, "}\n\n");
+
+ _editorPropsIter++;
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base.h b/engines/wintermute/base/base.h
new file mode 100644
index 0000000000..24820c748a
--- /dev/null
+++ b/engines/wintermute/base/base.h
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_BASE_H
+#define WINTERMUTE_BASE_BASE_H
+
+#include "engines/wintermute/wintypes.h"
+#include "engines/wintermute/dctypes.h"
+#include "common/str.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+namespace Wintermute {
+
+class BaseGame;
+class BaseDynamicBuffer;
+
+class BaseClass {
+public:
+ bool _persistable;
+ bool setEditorProp(const Common::String &propName, const Common::String &propValue);
+ Common::String getEditorProp(const Common::String &propName, const Common::String &initVal = NULL);
+ BaseClass(TDynamicConstructor, TDynamicConstructor) {}
+ bool parseEditorProperty(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0);
+ BaseClass();
+ BaseClass(BaseGame *GameOwner);
+ virtual ~BaseClass();
+ BaseGame *_gameRef;
+protected:
+ Common::HashMap<Common::String, Common::String> _editorProps;
+ Common::HashMap<Common::String, Common::String>::iterator _editorPropsIter;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_active_rect.cpp b/engines/wintermute/base/base_active_rect.cpp
new file mode 100644
index 0000000000..4addf15be8
--- /dev/null
+++ b/engines/wintermute/base/base_active_rect.cpp
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_active_rect.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/platform_osystem.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+BaseActiveRect::BaseActiveRect(BaseGame *inGame) : BaseClass(inGame) {
+ BasePlatform::setRectEmpty(&_rect);
+ _owner = NULL;
+ _frame = NULL;
+ _region = NULL;
+ _zoomX = 100;
+ _zoomY = 100;
+ _offsetX = _offsetY = 0;
+ clipRect();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseActiveRect::BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseSubFrame *frame, int x, int y, int width, int height, float zoomX, float zoomY, bool precise) : BaseClass(inGame) {
+ _owner = owner;
+ _frame = frame;
+ BasePlatform::setRect(&_rect, x, y, x + width, y + height);
+ _zoomX = zoomX;
+ _zoomY = zoomY;
+ _precise = precise;
+ _region = NULL;
+ _offsetX = _offsetY = 0;
+ clipRect();
+}
+
+//////////////////////////////////////////////////////////////////////
+BaseActiveRect::BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseRegion *region, int offsetX, int offsetY) : BaseClass(inGame) {
+ _owner = owner;
+ _region = region;
+ BasePlatform::copyRect(&_rect, &region->_rect);
+ _rect.offsetRect(-offsetX, -offsetY);
+ _zoomX = 100;
+ _zoomY = 100;
+ _precise = true;
+ _frame = NULL;
+ clipRect();
+ _offsetX = offsetX;
+ _offsetY = offsetY;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseActiveRect::~BaseActiveRect() {
+ _owner = NULL;
+ _frame = NULL;
+ _region = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseActiveRect::clipRect() {
+ Rect32 rc;
+ bool customViewport;
+ _gameRef->getCurrentViewportRect(&rc, &customViewport);
+ BaseRenderer *Rend = _gameRef->_renderer;
+
+ if (!customViewport) {
+ rc.left -= Rend->_drawOffsetX;
+ rc.right -= Rend->_drawOffsetX;
+ rc.top -= Rend->_drawOffsetY;
+ rc.bottom -= Rend->_drawOffsetY;
+ }
+
+ if (rc.left > _rect.left) {
+ _offsetX = rc.left - _rect.left;
+ }
+ if (rc.top > _rect.top) {
+ _offsetY = rc.top - _rect.top;
+ }
+
+ BasePlatform::intersectRect(&_rect, &_rect, &rc);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_active_rect.h b/engines/wintermute/base/base_active_rect.h
new file mode 100644
index 0000000000..fcd2619b03
--- /dev/null
+++ b/engines/wintermute/base/base_active_rect.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_ACTIVE_RECT_H
+#define WINTERMUTE_BASE_ACTIVE_RECT_H
+
+#include "engines/wintermute/math/rect32.h"
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+class BaseRegion;
+class BaseSubFrame;
+class BaseObject;
+class BaseActiveRect: BaseClass {
+public:
+ void clipRect();
+ bool _precise;
+ float _zoomX;
+ float _zoomY;
+ BaseSubFrame *_frame;
+ BaseObject *_owner;
+ BaseRegion *_region;
+ int _offsetX;
+ int _offsetY;
+ Rect32 _rect;
+ BaseActiveRect(BaseGame *inGameOwner = NULL);
+ BaseActiveRect(BaseGame *inGameOwner, BaseObject *owner, BaseSubFrame *frame, int x, int y, int width, int height, float zoomX = 100, float zoomY = 100, bool precise = true);
+ BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseRegion *region, int offsetX, int offsetY);
+ virtual ~BaseActiveRect();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_dynamic_buffer.cpp b/engines/wintermute/base/base_dynamic_buffer.cpp
new file mode 100644
index 0000000000..fc48e93c2b
--- /dev/null
+++ b/engines/wintermute/base/base_dynamic_buffer.cpp
@@ -0,0 +1,204 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+BaseDynamicBuffer::BaseDynamicBuffer(BaseGame *inGame, uint32 initSize, uint32 growBy) {
+ _buffer = NULL;
+ _size = 0;
+ _realSize = 0;
+
+ _offset = 0;
+ _initSize = initSize;
+ _growBy = growBy;
+
+ _initialized = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseDynamicBuffer::~BaseDynamicBuffer() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseDynamicBuffer::cleanup() {
+ if (_buffer) {
+ free(_buffer);
+ }
+ _buffer = NULL;
+ _size = 0;
+ _realSize = 0;
+ _offset = 0;
+ _initialized = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseDynamicBuffer::getSize() {
+ return _size;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseDynamicBuffer::init(uint32 initSize) {
+ cleanup();
+
+ if (initSize == 0) {
+ initSize = _initSize;
+ }
+
+ _buffer = (byte *)malloc(initSize);
+ if (!_buffer) {
+ BaseEngine::LOG(0, "BaseDynamicBuffer::Init - Error allocating %d bytes", initSize);
+ return STATUS_FAILED;
+ }
+
+ _realSize = initSize;
+ _initialized = true;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseDynamicBuffer::putBytes(const byte *buffer, uint32 size) {
+ if (!_initialized) {
+ init();
+ }
+
+ while (_offset + size > _realSize) {
+ _realSize += _growBy;
+ _buffer = (byte *)realloc(_buffer, _realSize);
+ if (!_buffer) {
+ BaseEngine::LOG(0, "BaseDynamicBuffer::PutBytes - Error reallocating buffer to %d bytes", _realSize);
+ return STATUS_FAILED;
+ }
+ }
+
+ memcpy(_buffer + _offset, buffer, size);
+ _offset += size;
+ _size += size;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseDynamicBuffer::getBytes(byte *buffer, uint32 size) {
+ if (!_initialized) {
+ init();
+ }
+
+ if (_offset + size > _size) {
+ BaseEngine::LOG(0, "BaseDynamicBuffer::GetBytes - Buffer underflow");
+ return STATUS_FAILED;
+ }
+
+ memcpy(buffer, _buffer + _offset, size);
+ _offset += size;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseDynamicBuffer::putDWORD(uint32 val) {
+ putBytes((byte *)&val, sizeof(uint32));
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseDynamicBuffer::getDWORD() {
+ uint32 ret;
+ getBytes((byte *)&ret, sizeof(uint32));
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseDynamicBuffer::putString(const char *val) {
+ if (!val) {
+ putString("(null)");
+ } else {
+ putDWORD(strlen(val) + 1);
+ putBytes((const byte *)val, strlen(val) + 1);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *BaseDynamicBuffer::getString() {
+ uint32 len = getDWORD();
+ char *ret = (char *)(_buffer + _offset);
+ _offset += len;
+
+ if (!strcmp(ret, "(null)")) {
+ return NULL;
+ } else {
+ return ret;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseDynamicBuffer::putText(const char *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ putTextForm(fmt, va);
+ va_end(va);
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseDynamicBuffer::putTextIndent(int indent, const char *fmt, ...) {
+ va_list va;
+
+ putText("%*s", indent, "");
+
+ va_start(va, fmt);
+ putTextForm(fmt, va);
+ va_end(va);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseDynamicBuffer::putTextForm(const char *format, va_list argptr) {
+ char buff[32768];
+ vsprintf(buff, format, argptr);
+ putBytes((byte *)buff, strlen(buff));
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_dynamic_buffer.h b/engines/wintermute/base/base_dynamic_buffer.h
new file mode 100644
index 0000000000..2d1a7fbe48
--- /dev/null
+++ b/engines/wintermute/base/base_dynamic_buffer.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_DYNAMIC_BUFFER_H
+#define WINTERMUTE_BASE_DYNAMIC_BUFFER_H
+
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+class BaseDynamicBuffer {
+public:
+ void putText(const char *fmt, ...);
+ void putTextIndent(int indent, const char *fmt, ...);
+ uint32 getDWORD();
+ void putDWORD(uint32 val);
+ char *getString();
+ void putString(const char *val);
+ bool getBytes(byte *buffer, uint32 size);
+ bool putBytes(const byte *buffer, uint32 size);
+ uint32 getSize();
+ bool init(uint32 initSize = 0);
+ void cleanup();
+ BaseDynamicBuffer(BaseGame *inGame, uint32 initSize = 1000, uint32 growBy = 1000);
+ virtual ~BaseDynamicBuffer();
+
+private:
+ uint32 _size;
+ byte *_buffer;
+ bool _initialized;
+ uint32 _realSize;
+ uint32 _growBy;
+ uint32 _initSize;
+ uint32 _offset;
+ void putTextForm(const char *format, va_list argptr);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_engine.cpp b/engines/wintermute/base/base_engine.cpp
new file mode 100644
index 0000000000..8146d14beb
--- /dev/null
+++ b/engines/wintermute/base/base_engine.cpp
@@ -0,0 +1,93 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/wintermute.h"
+#include "engines/wintermute/system/sys_class_registry.h"
+#include "common/system.h"
+namespace Common {
+DECLARE_SINGLETON(Wintermute::BaseEngine);
+}
+
+namespace Wintermute {
+
+BaseEngine::BaseEngine() {
+ _fileManager = NULL;
+ _gameRef = NULL;
+ _classReg = NULL;
+ _rnd = NULL;
+ _gameId = "";
+}
+
+void BaseEngine::init(Common::Language lang) {
+ _fileManager = new BaseFileManager(lang);
+ // Don't forget to register your random source
+ _rnd = new Common::RandomSource("Wintermute");
+ _classReg = new SystemClassRegistry();
+ _classReg->registerClasses();
+}
+
+BaseEngine::~BaseEngine() {
+ delete _fileManager;
+ delete _rnd;
+ delete _classReg;
+}
+
+void BaseEngine::createInstance(const Common::String &gameid, Common::Language lang) {
+ instance()._gameId = gameid;
+ instance().init(lang);
+}
+
+void BaseEngine::LOG(bool res, const char *fmt, ...) {
+ uint32 secs = g_system->getMillis() / 1000;
+ uint32 hours = secs / 3600;
+ secs = secs % 3600;
+ uint32 mins = secs / 60;
+ secs = secs % 60;
+
+ char buff[512];
+ va_list va;
+
+ va_start(va, fmt);
+ vsprintf(buff, fmt, va);
+ va_end(va);
+
+ if (instance()._gameRef) {
+ instance()._gameRef->LOG("%s", buff);
+ } else {
+ debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff);
+ }
+}
+
+uint32 BaseEngine::randInt(int from, int to) {
+ return _rnd->getRandomNumberRng(from, to);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h
new file mode 100644
index 0000000000..1ed0e3ab01
--- /dev/null
+++ b/engines/wintermute/base/base_engine.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_ENGINE_H
+#define WINTERMUTE_BASE_ENGINE_H
+
+#include "common/str.h"
+#include "common/singleton.h"
+#include "common/random.h"
+#include "common/language.h"
+
+namespace Wintermute {
+
+class BaseFileManager;
+class BaseRegistry;
+class BaseGame;
+class SystemClassRegistry;
+class BaseEngine : public Common::Singleton<Wintermute::BaseEngine> {
+ void init(Common::Language lang);
+ BaseFileManager *_fileManager;
+ Common::String _gameId;
+ BaseGame *_gameRef;
+ // We need random numbers
+ Common::RandomSource *_rnd;
+ SystemClassRegistry *_classReg;
+public:
+ BaseEngine();
+ ~BaseEngine();
+ static void createInstance(const Common::String &gameid, Common::Language lang);
+ void setGameRef(BaseGame *gameRef) { _gameRef = gameRef; }
+
+ Common::RandomSource *getRandomSource() { return _rnd; }
+ uint32 randInt(int from, int to);
+
+ SystemClassRegistry *getClassRegistry(){ return _classReg; }
+ BaseGame *getGameRef() { return _gameRef; }
+ BaseFileManager *getFileManager() { return _fileManager; }
+ static void LOG(bool res, const char *fmt, ...);
+ const char *getGameId() { return _gameId.c_str(); }
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_fader.cpp b/engines/wintermute/base/base_fader.cpp
new file mode 100644
index 0000000000..985718fcab
--- /dev/null
+++ b/engines/wintermute/base/base_fader.cpp
@@ -0,0 +1,195 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_fader.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "common/util.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(BaseFader, false)
+
+//////////////////////////////////////////////////////////////////////////
+BaseFader::BaseFader(BaseGame *inGame) : BaseObject(inGame) {
+ _active = false;
+ _red = _green = _blue = 0;
+ _currentAlpha = 0x00;
+ _sourceAlpha = 0;
+ _targetAlpha = 0;
+ _duration = 1000;
+ _startTime = 0;
+ _system = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseFader::~BaseFader() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFader::update() {
+ if (!_active) {
+ return STATUS_OK;
+ }
+
+ int alphaDelta = _targetAlpha - _sourceAlpha;
+
+ uint32 time;
+
+ if (_system) {
+ time = g_system->getMillis() - _startTime;
+ } else {
+ time = _gameRef->_timer - _startTime;
+ }
+
+ if (time >= _duration) {
+ _currentAlpha = _targetAlpha;
+ } else {
+ _currentAlpha = (byte)(_sourceAlpha + (float)time / (float)_duration * alphaDelta);
+ }
+ _currentAlpha = MIN((unsigned char)255, MAX(_currentAlpha, (byte)0)); // TODO: clean
+
+ _ready = time >= _duration;
+ if (_ready && _currentAlpha == 0x00) {
+ _active = false;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFader::display() {
+ if (!_active) {
+ return STATUS_OK;
+ }
+
+ if (_currentAlpha > 0x00) {
+ _gameRef->_renderer->fadeToColor(_red, _green, _blue, _currentAlpha);
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFader::deactivate() {
+ _active = false;
+ _ready = true;
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFader::fadeIn(uint32 sourceColor, uint32 duration, bool system) {
+ _ready = false;
+ _active = true;
+
+ _red = RGBCOLGetR(sourceColor);
+ _green = RGBCOLGetG(sourceColor);
+ _blue = RGBCOLGetB(sourceColor);
+
+ _sourceAlpha = RGBCOLGetA(sourceColor);
+ _targetAlpha = 0;
+
+ _duration = duration;
+ _system = system;
+
+ if (_system) {
+ _startTime = g_system->getMillis();
+ } else {
+ _startTime = _gameRef->_timer;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFader::fadeOut(uint32 targetColor, uint32 duration, bool system) {
+ _ready = false;
+ _active = true;
+
+ _red = RGBCOLGetR(targetColor);
+ _green = RGBCOLGetG(targetColor);
+ _blue = RGBCOLGetB(targetColor);
+
+ //_sourceAlpha = 0;
+ _sourceAlpha = _currentAlpha;
+ _targetAlpha = RGBCOLGetA(targetColor);
+
+ _duration = duration;
+ _system = system;
+
+ if (_system) {
+ _startTime = g_system->getMillis();
+ } else {
+ _startTime = _gameRef->_timer;
+ }
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseFader::getCurrentColor() {
+ return BYTETORGBA(_red, _green, _blue, _currentAlpha);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFader::persist(BasePersistenceManager *persistMgr) {
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_active));
+ persistMgr->transfer(TMEMBER(_blue));
+ persistMgr->transfer(TMEMBER(_currentAlpha));
+ persistMgr->transfer(TMEMBER(_duration));
+ persistMgr->transfer(TMEMBER(_green));
+ persistMgr->transfer(TMEMBER(_red));
+ persistMgr->transfer(TMEMBER(_sourceAlpha));
+ persistMgr->transfer(TMEMBER(_startTime));
+ persistMgr->transfer(TMEMBER(_targetAlpha));
+ persistMgr->transfer(TMEMBER(_system));
+
+ if (_system && !persistMgr->getIsSaving()) {
+ _startTime = 0;
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_fader.h b/engines/wintermute/base/base_fader.h
new file mode 100644
index 0000000000..116c8c963d
--- /dev/null
+++ b/engines/wintermute/base/base_fader.h
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FADER_H
+#define WINTERMUTE_BASE_FADER_H
+
+
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+
+class BaseFader : public BaseObject {
+public:
+ uint32 getCurrentColor();
+ bool fadeOut(uint32 targetColor, uint32 duration, bool system = false);
+ bool fadeIn(uint32 sourceColor, uint32 duration, bool system = false);
+ bool deactivate();
+ bool display();
+ bool update();
+ DECLARE_PERSISTENT(BaseFader, BaseObject)
+ BaseFader(BaseGame *inGame);
+ virtual ~BaseFader();
+private:
+ bool _system;
+ bool _active;
+ byte _red;
+ byte _green;
+ byte _blue;
+ byte _currentAlpha;
+ byte _targetAlpha;
+ byte _sourceAlpha;
+ uint32 _duration;
+ uint32 _startTime;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_file_manager.cpp b/engines/wintermute/base/base_file_manager.cpp
new file mode 100644
index 0000000000..b726c0c66f
--- /dev/null
+++ b/engines/wintermute/base/base_file_manager.cpp
@@ -0,0 +1,340 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+#include "engines/wintermute/base/file/base_disk_file.h"
+#include "engines/wintermute/base/file/base_save_thumb_file.h"
+#include "engines/wintermute/base/file/base_package.h"
+#include "engines/wintermute/base/file/base_resources.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/wintermute.h"
+#include "common/debug.h"
+#include "common/str.h"
+#include "common/tokenizer.h"
+#include "common/textconsole.h"
+#include "common/util.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "common/fs.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/fs.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////
+BaseFileManager::BaseFileManager(Common::Language lang) {
+ _language = lang;
+ initPaths();
+ registerPackages();
+}
+
+//////////////////////////////////////////////////////////////////////
+BaseFileManager::~BaseFileManager() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFileManager::cleanup() {
+ // delete registered paths
+ _packagePaths.clear();
+
+ // close open files
+ for (uint32 i = 0; i < _openFiles.size(); i++) {
+ delete _openFiles[i];
+ }
+ _openFiles.clear();
+
+ // delete packages
+ _packages.clear();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////
+byte *BaseFileManager::readWholeFile(const Common::String &filename, uint32 *size, bool mustExist) {
+ byte *buffer = NULL;
+
+ Common::SeekableReadStream *file = openFile(filename);
+ if (!file) {
+ if (mustExist) {
+ debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error opening file '%s'", filename.c_str());
+ }
+ return NULL;
+ }
+
+ buffer = new byte[file->size() + 1];
+ if (buffer == NULL) {
+ debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error allocating buffer for file '%s' (%d bytes)", filename.c_str(), file->size() + 1);
+ closeFile(file);
+ return NULL;
+ }
+
+ if (file->read(buffer, (uint32)file->size()) != (uint32)file->size()) {
+ debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error reading file '%s'", filename.c_str());
+ closeFile(file);
+ delete[] buffer;
+ return NULL;
+ };
+
+ buffer[file->size()] = '\0';
+ if (size != NULL) {
+ *size = file->size();
+ }
+ closeFile(file);
+
+ return buffer;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFileManager::addPath(TPathType type, const Common::FSNode &path) {
+ if (!path.exists()) {
+ return STATUS_FAILED;
+ }
+
+ switch (type) {
+ case PATH_SINGLE:
+ // _singlePaths.push_back(path);
+ error("TODO: Allow adding single-paths");
+ break;
+ case PATH_PACKAGE:
+ _packagePaths.push_back(path);
+ break;
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFileManager::reloadPaths() {
+ // delete registered paths
+ //_singlePaths.clear();
+ _packagePaths.clear();
+
+ return initPaths();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFileManager::initPaths() {
+ // Removed: Config-based file-path choice.
+
+ // package files paths
+ const Common::FSNode gameData(ConfMan.get("path"));
+ addPath(PATH_PACKAGE, gameData);
+
+ Common::FSNode dataSubFolder = gameData.getChild("data");
+ if (dataSubFolder.exists()) {
+ addPath(PATH_PACKAGE, dataSubFolder);
+ }
+ Common::FSNode languageSubFolder = gameData.getChild("language");
+ if (languageSubFolder.exists()) {
+ addPath(PATH_PACKAGE, languageSubFolder);
+ }
+ return STATUS_OK;
+}
+
+bool BaseFileManager::registerPackages(const Common::FSList &fslist) {
+ for (Common::FSList::const_iterator it = fslist.begin(); it != fslist.end(); ++it) {
+ debugC(kWintermuteDebugFileAccess, "Adding %s", (*it).getName().c_str());
+ if ((*it).getName().contains(".dcp")) {
+ if (registerPackage((*it))) {
+ addPath(PATH_PACKAGE, (*it));
+ }
+ }
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFileManager::registerPackages() {
+ debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Scanning packages");
+
+ // Register without using SearchMan, as otherwise the FSNode-based lookup in openPackage will fail
+ // and that has to be like that to support the detection-scheme.
+ Common::FSList files;
+ for (Common::FSList::iterator it = _packagePaths.begin(); it != _packagePaths.end(); ++it) {
+ debugC(kWintermuteDebugFileAccess, "Should register folder: %s %s", (*it).getPath().c_str(), (*it).getName().c_str());
+ (*it).getChildren(files, Common::FSNode::kListFilesOnly);
+ for (Common::FSList::iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
+ if (!fileIt->getName().hasSuffix(".dcp")) {
+ continue;
+ }
+ // Avoid registering all the language files
+ // TODO: Select based on the gameDesc.
+ if (_language != Common::UNK_LANG && fileIt->getParent().getName() == "language") {
+ Common::String parentName = fileIt->getParent().getName();
+ Common::String dcpName = fileIt->getName();
+ if (_language == Common::EN_ANY && fileIt->getName() != "english.dcp") {
+ continue;
+ } else if (_language == Common::CZ_CZE && fileIt->getName() != "czech.dcp") {
+ continue;
+ } else if (_language == Common::IT_ITA && fileIt->getName() != "italian.dcp") {
+ continue;
+ } else if (_language == Common::PL_POL && fileIt->getName() != "polish.dcp") {
+ continue;
+ } else if (_language == Common::RU_RUS && fileIt->getName() != "russian.dcp") {
+ continue;
+ }
+ }
+ debugC(kWintermuteDebugFileAccess, "Registering %s %s", (*fileIt).getPath().c_str(), (*fileIt).getName().c_str());
+ registerPackage((*fileIt));
+ }
+ }
+
+// debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Registered %d files in %d package(s)", _files.size(), _packages.size());
+
+ return STATUS_OK;
+}
+
+bool BaseFileManager::registerPackage(Common::FSNode file, const Common::String &filename, bool searchSignature) {
+ PackageSet *pack = new PackageSet(file, filename, searchSignature);
+ _packages.add(file.getName(), pack, pack->getPriority() , true);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+Common::SeekableReadStream *BaseFileManager::openPkgFile(const Common::String &filename) {
+ Common::String upcName = filename;
+ upcName.toUppercase();
+ Common::SeekableReadStream *file = NULL;
+ char fileName[MAX_PATH_LENGTH];
+ strcpy(fileName, upcName.c_str());
+
+ // correct slashes
+ for (uint32 i = 0; i < upcName.size(); i++) {
+ if (upcName[(int32)i] == '/') {
+ upcName.setChar('\\', (uint32)i);
+ }
+ }
+ Common::ArchiveMemberPtr entry = _packages.getMember(upcName);
+ if (!entry) {
+ return NULL;
+ }
+ file = entry->createReadStream();
+ return file;
+}
+
+bool BaseFileManager::hasFile(const Common::String &filename) {
+ if (scumm_strnicmp(filename.c_str(), "savegame:", 9) == 0) {
+ BasePersistenceManager pm(BaseEngine::instance().getGameId());
+ if (filename.size() <= 9) {
+ return false;
+ }
+ int slot = atoi(filename.c_str() + 9);
+ return pm.getSaveExists(slot);
+ }
+ if (diskFileExists(filename)) {
+ return true;
+ }
+ if (_packages.hasFile(filename)) {
+ return true; // We don't bother checking if the file can actually be opened, something bigger is wrong if that is the case.
+ }
+ if (BaseResources::hasFile(filename)) {
+ return true;
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+Common::SeekableReadStream *BaseFileManager::openFile(const Common::String &filename, bool absPathWarning, bool keepTrackOf) {
+ if (strcmp(filename.c_str(), "") == 0) {
+ return NULL;
+ }
+ debugC(kWintermuteDebugFileAccess, "Open file %s", filename.c_str());
+
+ Common::SeekableReadStream *file = openFileRaw(filename);
+ if (file && keepTrackOf) {
+ _openFiles.push_back(file);
+ }
+ return file;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFileManager::closeFile(Common::SeekableReadStream *File) {
+ for (uint32 i = 0; i < _openFiles.size(); i++) {
+ if (_openFiles[i] == File) {
+ delete _openFiles[i];
+ _openFiles.remove_at(i);
+ return STATUS_OK;
+ }
+ }
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+Common::SeekableReadStream *BaseFileManager::openFileRaw(const Common::String &filename) {
+ Common::SeekableReadStream *ret = NULL;
+
+ if (scumm_strnicmp(filename.c_str(), "savegame:", 9) == 0) {
+ if (!BaseEngine::instance().getGameRef()) {
+ error("Attempt to load filename: %s without BaseEngine-object, this is unsupported", filename.c_str());
+ }
+ BaseSaveThumbFile *saveThumbFile = new BaseSaveThumbFile();
+ if (DID_SUCCEED(saveThumbFile->open(filename))) {
+ ret = saveThumbFile->getMemStream();
+ }
+ delete saveThumbFile;
+ return ret;
+ }
+
+ ret = openDiskFile(filename);
+ if (ret) {
+ return ret;
+ }
+
+ ret = openPkgFile(filename);
+ if (ret) {
+ return ret;
+ }
+
+ ret = BaseResources::getFile(filename);
+ if (ret) {
+ return ret;
+ }
+
+ debugC(kWintermuteDebugFileAccess ,"BFileManager::OpenFileRaw - Failed to open %s", filename.c_str());
+ return NULL;
+}
+
+BaseFileManager *BaseFileManager::getEngineInstance() {
+ if (BaseEngine::instance().getFileManager()) {
+ return BaseEngine::instance().getFileManager();
+ }
+ return NULL;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_file_manager.h b/engines/wintermute/base/base_file_manager.h
new file mode 100644
index 0000000000..70aff49bbb
--- /dev/null
+++ b/engines/wintermute/base/base_file_manager.h
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FILE_MANAGER_H
+#define WINTERMUTE_BASE_FILE_MANAGER_H
+
+#include "common/archive.h"
+#include "common/str.h"
+#include "common/fs.h"
+#include "common/file.h"
+#include "common/language.h"
+
+namespace Wintermute {
+class BaseFileManager {
+public:
+ bool cleanup();
+
+ bool closeFile(Common::SeekableReadStream *File);
+ bool hasFile(const Common::String &filename);
+ Common::SeekableReadStream *openFile(const Common::String &filename, bool absPathWarning = true, bool keepTrackOf = true);
+ byte *readWholeFile(const Common::String &filename, uint32 *size = NULL, bool mustExist = true);
+
+ BaseFileManager(Common::Language lang);
+ virtual ~BaseFileManager();
+ // Used only for detection
+ bool registerPackages(const Common::FSList &fslist);
+ static BaseFileManager *getEngineInstance();
+private:
+ typedef enum {
+ PATH_PACKAGE,
+ PATH_SINGLE
+ } TPathType;
+ bool reloadPaths();
+ bool initPaths();
+ bool addPath(TPathType type, const Common::FSNode &path);
+ bool registerPackages();
+ Common::SeekableReadStream *openFileRaw(const Common::String &filename);
+ Common::SeekableReadStream *openPkgFile(const Common::String &filename);
+ Common::FSList _packagePaths;
+ bool findPackageSignature(Common::SeekableReadStream *f, uint32 *offset);
+ bool registerPackage(Common::FSNode package, const Common::String &filename = "", bool searchSignature = false);
+ Common::SearchSet _packages;
+ Common::Array<Common::SeekableReadStream *> _openFiles;
+ Common::Language _language;
+ // This class is intentionally not a subclass of Base, as it needs to be used by
+ // the detector too, without launching the entire engine:
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_frame.cpp b/engines/wintermute/base/base_frame.cpp
new file mode 100644
index 0000000000..7c64144480
--- /dev/null
+++ b/engines/wintermute/base/base_frame.cpp
@@ -0,0 +1,766 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_frame.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseFrame, false)
+
+//////////////////////////////////////////////////////////////////////
+BaseFrame::BaseFrame(BaseGame *inGame) : BaseScriptable(inGame, true) {
+ _delay = 0;
+ _moveX = _moveY = 0;
+
+ _sound = NULL;
+ _killSound = false;
+
+ _editorExpanded = false;
+ _keyframe = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseFrame::~BaseFrame() {
+ delete _sound;
+ _sound = NULL;
+
+ for (uint32 i = 0; i < _subframes.size(); i++) {
+ delete _subframes[i];
+ }
+ _subframes.clear();
+
+ for (uint32 i = 0; i < _applyEvent.size(); i++) {
+ delete[] _applyEvent[i];
+ _applyEvent[i] = NULL;
+ }
+ _applyEvent.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, bool precise, uint32 alpha, bool allFrames, float rotate, TSpriteBlendMode blendMode) {
+ bool res;
+
+ for (uint32 i = 0; i < _subframes.size(); i++) {
+ res = _subframes[i]->draw(x, y, registerOwner, zoomX, zoomY, precise, alpha, rotate, blendMode);
+ if (DID_FAIL(res)) {
+ return res;
+ }
+ }
+ return STATUS_OK;
+}
+
+void BaseFrame::stopSound() {
+ if (_sound) {
+ _sound->stop();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFrame::oneTimeDisplay(BaseObject *owner, bool muted) {
+ if (_sound && !muted) {
+ if (owner) {
+ owner->updateOneSound(_sound);
+ }
+ _sound->play();
+ /*
+ if (_gameRef->_state == GAME_FROZEN) {
+ _sound->Pause(true);
+ }
+ */
+ }
+ if (owner) {
+ for (uint32 i = 0; i < _applyEvent.size(); i++) {
+ owner->applyEvent(_applyEvent[i]);
+ }
+ }
+ return STATUS_OK;
+}
+
+
+
+TOKEN_DEF_START
+TOKEN_DEF(DELAY)
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(TRANSPARENT)
+TOKEN_DEF(RECT)
+TOKEN_DEF(HOTSPOT)
+TOKEN_DEF(2D_ONLY)
+TOKEN_DEF(3D_ONLY)
+TOKEN_DEF(MIRROR_X)
+TOKEN_DEF(MIRROR_Y)
+TOKEN_DEF(MOVE)
+TOKEN_DEF(ALPHA_COLOR)
+TOKEN_DEF(ALPHA)
+TOKEN_DEF(SUBFRAME)
+TOKEN_DEF(SOUND)
+TOKEN_DEF(KEYFRAME)
+TOKEN_DEF(DECORATION)
+TOKEN_DEF(APPLY_EVENT)
+TOKEN_DEF(EDITOR_SELECTED)
+TOKEN_DEF(EDITOR_EXPANDED)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(KILL_SOUND)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////
+bool BaseFrame::loadBuffer(byte *buffer, int lifeTime, bool keepLoaded) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(DELAY)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(TRANSPARENT)
+ TOKEN_TABLE(RECT)
+ TOKEN_TABLE(HOTSPOT)
+ TOKEN_TABLE(2D_ONLY)
+ TOKEN_TABLE(3D_ONLY)
+ TOKEN_TABLE(MIRROR_X)
+ TOKEN_TABLE(MIRROR_Y)
+ TOKEN_TABLE(MOVE)
+ TOKEN_TABLE(ALPHA_COLOR)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE(SUBFRAME)
+ TOKEN_TABLE(SOUND)
+ TOKEN_TABLE(KEYFRAME)
+ TOKEN_TABLE(DECORATION)
+ TOKEN_TABLE(APPLY_EVENT)
+ TOKEN_TABLE(EDITOR_SELECTED)
+ TOKEN_TABLE(EDITOR_EXPANDED)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(KILL_SOUND)
+ TOKEN_TABLE_END
+
+ char *params;
+ int cmd;
+ BaseParser parser;
+ Rect32 rect;
+ int r = 255, g = 255, b = 255;
+ int ar = 255, ag = 255, ab = 255, alpha = 255;
+ int hotspotX = 0, hotspotY = 0;
+ bool custoTrans = false;
+ bool editorSelected = false;
+ bool is2DOnly = false;
+ bool is3DOnly = false;
+ bool decoration = false;
+ bool mirrorX = false;
+ bool mirrorY = false;
+ BasePlatform::setRectEmpty(&rect);
+ char *surface_file = NULL;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, &params)) > 0) {
+ switch (cmd) {
+ case TOKEN_DELAY:
+ parser.scanStr(params, "%d", &_delay);
+ break;
+
+ case TOKEN_IMAGE:
+ surface_file = params;
+ break;
+
+ case TOKEN_TRANSPARENT:
+ parser.scanStr(params, "%d,%d,%d", &r, &g, &b);
+ custoTrans = true;
+ break;
+
+ case TOKEN_RECT:
+ parser.scanStr(params, "%d,%d,%d,%d", &rect.left, &rect.top, &rect.right, &rect.bottom);
+ break;
+
+ case TOKEN_HOTSPOT:
+ parser.scanStr(params, "%d,%d", &hotspotX, &hotspotY);
+ break;
+
+ case TOKEN_MOVE:
+ parser.scanStr(params, "%d,%d", &_moveX, &_moveY);
+ break;
+
+ case TOKEN_2D_ONLY:
+ parser.scanStr(params, "%b", &is2DOnly);
+ break;
+
+ case TOKEN_3D_ONLY:
+ parser.scanStr(params, "%b", &is3DOnly);
+ break;
+
+ case TOKEN_MIRROR_X:
+ parser.scanStr(params, "%b", &mirrorX);
+ break;
+
+ case TOKEN_MIRROR_Y:
+ parser.scanStr(params, "%b", &mirrorY);
+ break;
+
+ case TOKEN_ALPHA_COLOR:
+ parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab);
+ break;
+
+ case TOKEN_ALPHA:
+ parser.scanStr(params, "%d", &alpha);
+ break;
+
+ case TOKEN_EDITOR_SELECTED:
+ parser.scanStr(params, "%b", &editorSelected);
+ break;
+
+ case TOKEN_EDITOR_EXPANDED:
+ parser.scanStr(params, "%b", &_editorExpanded);
+ break;
+
+ case TOKEN_KILL_SOUND:
+ parser.scanStr(params, "%b", &_killSound);
+ break;
+
+ case TOKEN_SUBFRAME: {
+ BaseSubFrame *subframe = new BaseSubFrame(_gameRef);
+ if (!subframe || DID_FAIL(subframe->loadBuffer((byte *)params, lifeTime, keepLoaded))) {
+ delete subframe;
+ cmd = PARSERR_GENERIC;
+ } else {
+ _subframes.add(subframe);
+ }
+ }
+ break;
+
+ case TOKEN_SOUND: {
+ if (_sound) {
+ delete _sound;
+ _sound = NULL;
+ }
+ _sound = new BaseSound(_gameRef);
+ if (!_sound || DID_FAIL(_sound->setSound(params, Audio::Mixer::kSFXSoundType, false))) {
+ if (_gameRef->_soundMgr->_soundAvailable) {
+ _gameRef->LOG(0, "Error loading sound '%s'.", params);
+ }
+ delete _sound;
+ _sound = NULL;
+ }
+ }
+ break;
+
+ case TOKEN_APPLY_EVENT: {
+ char *event = new char[strlen(params) + 1];
+ strcpy(event, params);
+ _applyEvent.add(event);
+ }
+ break;
+
+ case TOKEN_KEYFRAME:
+ parser.scanStr(params, "%b", &_keyframe);
+ break;
+
+ case TOKEN_DECORATION:
+ parser.scanStr(params, "%b", &decoration);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty((byte *)params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in FRAME definition");
+ return STATUS_FAILED;
+ }
+
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading FRAME definition");
+ return STATUS_FAILED;
+ }
+
+
+ BaseSubFrame *sub = new BaseSubFrame(_gameRef);
+ if (surface_file != NULL) {
+ if (custoTrans) {
+ sub->setSurface(surface_file, false, r, g, b, lifeTime, keepLoaded);
+ } else {
+ sub->setSurface(surface_file, true, 0, 0, 0, lifeTime, keepLoaded);
+ }
+
+ if (!sub->_surface) {
+ delete sub;
+ _gameRef->LOG(0, "Error loading SUBFRAME");
+ return STATUS_FAILED;
+ }
+
+ sub->_alpha = BYTETORGBA(ar, ag, ab, alpha);
+ if (custoTrans) {
+ sub->_transparent = BYTETORGBA(r, g, b, 0xFF);
+ }
+ }
+
+ if (BasePlatform::isRectEmpty(&rect)) {
+ sub->setDefaultRect();
+ } else {
+ sub->setRect(rect);
+ }
+
+ sub->_hotspotX = hotspotX;
+ sub->_hotspotY = hotspotY;
+ sub->_2DOnly = is2DOnly;
+ sub->_3DOnly = is3DOnly;
+ sub->_decoration = decoration;
+ sub->_mirrorX = mirrorX;
+ sub->_mirrorY = mirrorY;
+
+
+ sub->_editorSelected = editorSelected;
+ _subframes.insert_at(0, sub);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFrame::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) {
+ if (!rect) {
+ return false;
+ }
+ BasePlatform::setRectEmpty(rect);
+
+ Rect32 subRect;
+
+ for (uint32 i = 0; i < _subframes.size(); i++) {
+ _subframes[i]->getBoundingRect(&subRect, x, y, scaleX, scaleY);
+ BasePlatform::unionRect(rect, rect, &subRect);
+ }
+ return true;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFrame::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "FRAME {\n");
+ buffer->putTextIndent(indent + 2, "DELAY = %d\n", _delay);
+
+ if (_moveX != 0 || _moveY != 0) {
+ buffer->putTextIndent(indent + 2, "MOVE {%d, %d}\n", _moveX, _moveY);
+ }
+
+ if (_sound && _sound->getFilename()) {
+ buffer->putTextIndent(indent + 2, "SOUND=\"%s\"\n", _sound->getFilename());
+ }
+
+ buffer->putTextIndent(indent + 2, "KEYFRAME=%s\n", _keyframe ? "TRUE" : "FALSE");
+
+ if (_killSound) {
+ buffer->putTextIndent(indent + 2, "KILL_SOUND=%s\n", _killSound ? "TRUE" : "FALSE");
+ }
+
+ if (_editorExpanded) {
+ buffer->putTextIndent(indent + 2, "EDITOR_EXPANDED=%s\n", _editorExpanded ? "TRUE" : "FALSE");
+ }
+
+ if (_subframes.size() > 0) {
+ _subframes[0]->saveAsText(buffer, indent, false);
+ }
+
+ for (uint32 i = 1; i < _subframes.size(); i++) {
+ _subframes[i]->saveAsText(buffer, indent + 2);
+ }
+
+ for (uint32 i = 0; i < _applyEvent.size(); i++) {
+ buffer->putTextIndent(indent + 2, "APPLY_EVENT=\"%s\"\n", _applyEvent[i]);
+ }
+
+ BaseClass::saveAsText(buffer, indent + 2);
+
+
+ buffer->putTextIndent(indent, "}\n\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFrame::persist(BasePersistenceManager *persistMgr) {
+ BaseScriptable::persist(persistMgr);
+
+ _applyEvent.persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_delay));
+ persistMgr->transfer(TMEMBER(_editorExpanded));
+ persistMgr->transfer(TMEMBER(_keyframe));
+ persistMgr->transfer(TMEMBER(_killSound));
+ persistMgr->transfer(TMEMBER(_moveX));
+ persistMgr->transfer(TMEMBER(_moveY));
+ persistMgr->transfer(TMEMBER(_sound));
+ _subframes.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSound
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetSound") == 0) {
+ stack->correctParams(0);
+
+ if (_sound && _sound->getFilename()) {
+ stack->pushString(_sound->getFilename());
+ } else {
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetSound
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetSound") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ delete _sound;
+ _sound = NULL;
+
+ if (!val->isNULL()) {
+ _sound = new BaseSound(_gameRef);
+ if (!_sound || DID_FAIL(_sound->setSound(val->getString(), Audio::Mixer::kSFXSoundType, false))) {
+ stack->pushBool(false);
+ delete _sound;
+ _sound = NULL;
+ } else {
+ stack->pushBool(true);
+ }
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSubframe
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetSubframe") == 0) {
+ stack->correctParams(1);
+ int index = stack->pop()->getInt(-1);
+ if (index < 0 || index >= (int32)_subframes.size()) {
+ script->runtimeError("Frame.GetSubframe: Subframe index %d is out of range.", index);
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_subframes[index], true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteSubframe
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteSubframe") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ if (val->isInt()) {
+ int index = val->getInt(-1);
+ if (index < 0 || index >= (int32)_subframes.size()) {
+ script->runtimeError("Frame.DeleteSubframe: Subframe index %d is out of range.", index);
+ }
+ } else {
+ BaseSubFrame *sub = (BaseSubFrame *)val->getNative();
+ for (uint32 i = 0; i < _subframes.size(); i++) {
+ if (_subframes[i] == sub) {
+ delete _subframes[i];
+ _subframes.remove_at(i);
+ break;
+ }
+ }
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddSubframe
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddSubframe") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ const char *filename = NULL;
+ if (!val->isNULL()) {
+ filename = val->getString();
+ }
+
+ BaseSubFrame *sub = new BaseSubFrame(_gameRef);
+ if (filename != NULL) {
+ sub->setSurface(filename);
+ sub->setDefaultRect();
+ }
+ _subframes.add(sub);
+
+ stack->pushNative(sub, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InsertSubframe
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InsertSubframe") == 0) {
+ stack->correctParams(2);
+ int index = stack->pop()->getInt();
+ if (index < 0) {
+ index = 0;
+ }
+
+ ScValue *val = stack->pop();
+ const char *filename = NULL;
+ if (!val->isNULL()) {
+ filename = val->getString();
+ }
+
+ BaseSubFrame *sub = new BaseSubFrame(_gameRef);
+ if (filename != NULL) {
+ sub->setSurface(filename);
+ }
+
+ if (index >= (int32)_subframes.size()) {
+ _subframes.add(sub);
+ } else {
+ _subframes.insert_at(index, sub);
+ }
+
+ stack->pushNative(sub, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetEvent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSubframe") == 0) {
+ stack->correctParams(1);
+ int index = stack->pop()->getInt(-1);
+ if (index < 0 || index >= (int32)_applyEvent.size()) {
+ script->runtimeError("Frame.GetEvent: Event index %d is out of range.", index);
+ stack->pushNULL();
+ } else {
+ stack->pushString(_applyEvent[index]);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddEvent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddEvent") == 0) {
+ stack->correctParams(1);
+ const char *event = stack->pop()->getString();
+ for (uint32 i = 0; i < _applyEvent.size(); i++) {
+ if (scumm_stricmp(_applyEvent[i], event) == 0) {
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ }
+ _applyEvent.add(event);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteEvent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteEvent") == 0) {
+ stack->correctParams(1);
+ const char *event = stack->pop()->getString();
+ for (uint32 i = 0; i < _applyEvent.size(); i++) {
+ if (scumm_stricmp(_applyEvent[i], event) == 0) {
+ delete[] _applyEvent[i];
+ _applyEvent.remove_at(i);
+ break;
+ }
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ if (_subframes.size() == 1) {
+ return _subframes[0]->scCallMethod(script, stack, thisStack, name);
+ } else {
+ return BaseScriptable::scCallMethod(script, stack, thisStack, name);
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseFrame::scGetProperty(const Common::String &name) {
+ if (!_scValue) {
+ _scValue = new ScValue(_gameRef);
+ }
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("frame");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Delay
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Delay") {
+ _scValue->setInt(_delay);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Keyframe
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Keyframe") {
+ _scValue->setBool(_keyframe);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // KillSounds
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "KillSounds") {
+ _scValue->setBool(_killSound);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MoveX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MoveX") {
+ _scValue->setInt(_moveX);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MoveY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MoveY") {
+ _scValue->setInt(_moveY);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumSubframes (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumSubframes") {
+ _scValue->setInt(_subframes.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumEvents (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumEvents") {
+ _scValue->setInt(_applyEvent.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ if (_subframes.size() == 1) {
+ return _subframes[0]->scGetProperty(name);
+ } else {
+ return BaseScriptable::scGetProperty(name);
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFrame::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Delay
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Delay") == 0) {
+ _delay = MAX(0, value->getInt());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Keyframe
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Keyframe") == 0) {
+ _keyframe = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // KillSounds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "KillSounds") == 0) {
+ _killSound = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MoveX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MoveX") == 0) {
+ _moveX = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MoveY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MoveY") == 0) {
+ _moveY = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ if (_subframes.size() == 1) {
+ return _subframes[0]->scSetProperty(name, value);
+ } else {
+ return BaseScriptable::scSetProperty(name, value);
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseFrame::scToString() {
+ return "[frame]";
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_frame.h b/engines/wintermute/base/base_frame.h
new file mode 100644
index 0000000000..7c5d893e70
--- /dev/null
+++ b/engines/wintermute/base/base_frame.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FRAME_H
+#define WINTERMUTE_BASE_FRAME_H
+
+#include "engines/wintermute/base/base_scriptable.h"
+#include "engines/wintermute/coll_templ.h"
+
+namespace Wintermute {
+class BaseSound;
+class BaseSubFrame;
+class BaseObject;
+class ScScript;
+class ScStack;
+class BaseFrame: public BaseScriptable {
+public:
+ bool _killSound;
+ void stopSound();
+ bool oneTimeDisplay(BaseObject *owner, bool muted = false);
+ DECLARE_PERSISTENT(BaseFrame, BaseScriptable)
+
+ bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100);
+ bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ int _moveY;
+ int _moveX;
+ uint32 _delay;
+ BaseArray<BaseSubFrame *> _subframes;
+ bool draw(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, bool precise = true, uint32 alpha = 0xFFFFFFFF, bool allFrames = false, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL);
+ bool loadBuffer(byte *buffer, int lifeTime, bool keepLoaded);
+
+ BaseFrame(BaseGame *inGame);
+ virtual ~BaseFrame();
+
+ BaseArray<const char *> _applyEvent;
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+private:
+ bool _keyframe;
+ bool _editorExpanded;
+ BaseSound *_sound;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_game.cpp b/engines/wintermute/base/base_game.cpp
new file mode 100644
index 0000000000..f0b1171ca4
--- /dev/null
+++ b/engines/wintermute/base/base_game.cpp
@@ -0,0 +1,4483 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/dcgf.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_fader.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/base_keyboard_state.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_quick_msg.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/base_transition_manager.h"
+#include "engines/wintermute/base/base_viewport.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/base_save_thumb_helper.h"
+#include "engines/wintermute/base/base_surface_storage.h"
+#include "engines/wintermute/base/saveload.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_engine.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_ext_math.h"
+#include "engines/wintermute/video/video_player.h"
+#include "engines/wintermute/video/video_theora_player.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/utils/crc.h"
+#include "engines/wintermute/utils/path_util.h"
+#include "engines/wintermute/utils/string_util.h"
+#include "engines/wintermute/ui/ui_window.h"
+#include "engines/wintermute/wintermute.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/textconsole.h"
+#include "common/util.h"
+#include "common/keyboard.h"
+#include "common/system.h"
+#include "common/file.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(BaseGame, true)
+
+
+//////////////////////////////////////////////////////////////////////
+BaseGame::BaseGame(const Common::String &gameId) : BaseObject(this), _gameId(gameId) {
+ _shuttingDown = false;
+
+ _state = GAME_RUNNING;
+ _origState = GAME_RUNNING;
+ _freezeLevel = 0;
+
+ _interactive = true;
+ _origInteractive = false;
+
+ _surfaceStorage = NULL;
+ _fontStorage = NULL;
+ _renderer = NULL;
+ _soundMgr = NULL;
+ _transMgr = NULL;
+ _scEngine = NULL;
+ _keyboardState = NULL;
+
+ _mathClass = NULL;
+
+ _debugLogFile = NULL;
+ _debugDebugMode = false;
+ _debugShowFPS = false;
+
+ _systemFont = NULL;
+ _videoFont = NULL;
+
+ _videoPlayer = NULL;
+ _theoraPlayer = NULL;
+
+ _mainObject = NULL;
+ _activeObject = NULL;
+
+ _fader = NULL;
+
+ _offsetX = _offsetY = 0;
+ _offsetPercentX = _offsetPercentY = 0.0f;
+
+ _subtitles = true;
+ _videoSubtitles = true;
+
+ _timer = 0;
+ _timerDelta = 0;
+ _timerLast = 0;
+
+ _liveTimer = 0;
+ _liveTimerDelta = 0;
+ _liveTimerLast = 0;
+
+ _sequence = 0;
+
+ _mousePos.x = _mousePos.y = 0;
+ _mouseLeftDown = _mouseRightDown = _mouseMidlleDown = false;
+ _capturedObject = NULL;
+
+ // FPS counters
+ _lastTime = _fpsTime = _deltaTime = _framesRendered = _fps = 0;
+
+ _cursorNoninteractive = NULL;
+
+ _useD3D = false;
+
+ _stringTable = new BaseStringTable(this);
+
+ for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) {
+ _music[i] = NULL;
+ _musicStartTime[i] = 0;
+ }
+
+ _settingsResWidth = 800;
+ _settingsResHeight = 600;
+ _settingsRequireAcceleration = false;
+ _settingsRequireSound = false;
+ _settingsTLMode = 0;
+ _settingsAllowWindowed = true;
+ _settingsGameFile = NULL;
+ _settingsAllowAdvanced = false;
+ _settingsAllowAccessTab = true;
+ _settingsAllowAboutTab = true;
+ _settingsAllowDesktopRes = false;
+
+ _editorForceScripts = false;
+ _editorAlwaysRegister = false;
+
+ _focusedWindow = NULL;
+
+ _loadInProgress = false;
+
+ _quitting = false;
+ _loading = false;
+ _scheduledLoadSlot = -1;
+
+ _personalizedSave = false;
+ _compressedSavegames = true;
+
+ _editorMode = false;
+ //_doNotExpandStrings = false;
+
+ _engineLogCallback = NULL;
+ _engineLogCallbackData = NULL;
+
+ _smartCache = false;
+ _surfaceGCCycleTime = 10000;
+
+ _reportTextureFormat = false;
+
+ _viewportSP = -1;
+
+ _subtitlesSpeed = 70;
+
+ _forceNonStreamedSounds = false;
+
+ _thumbnailWidth = _thumbnailHeight = 0;
+
+ _richSavedGames = false;
+ _savedGameExt = NULL;
+ BaseUtils::setString(&_savedGameExt, "dsv");
+
+ _musicCrossfadeRunning = false;
+ _musicCrossfadeStartTime = 0;
+ _musicCrossfadeLength = 0;
+ _musicCrossfadeChannel1 = -1;
+ _musicCrossfadeChannel2 = -1;
+ _musicCrossfadeSwap = false;
+
+ _localSaveDir = NULL;
+ BaseUtils::setString(&_localSaveDir, "saves");
+ _saveDirChecked = false;
+
+ _loadingIcon = NULL;
+ _loadingIconX = _loadingIconY = 0;
+ _loadingIconPersistent = false;
+
+ _textEncoding = TEXT_ANSI;
+ _textRTL = false;
+
+ _soundBufferSizeSec = 3;
+ _suspendedRendering = false;
+
+ _lastCursor = NULL;
+
+
+ BasePlatform::setRectEmpty(&_mouseLockRect);
+
+ _suppressScriptErrors = false;
+ _lastMiniUpdate = 0;
+ _miniUpdateEnabled = false;
+
+ _cachedThumbnail = NULL;
+
+ _autorunDisabled = false;
+
+ // compatibility bits
+ _compatKillMethodThreads = false;
+
+ _usedMem = 0;
+
+
+ _autoSaveOnExit = true;
+ _autoSaveSlot = 999;
+ _cursorHidden = false;
+
+ // Block kept as a reminder that the engine CAN run in constrained/touch-mode
+ /*#ifdef __IPHONEOS__
+ _touchInterface = true;
+ _constrainedMemory = true; // TODO differentiate old and new iOS devices
+ #else*/
+ _touchInterface = false;
+ _constrainedMemory = false;
+//#endif
+
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseGame::~BaseGame() {
+ _shuttingDown = true;
+
+ LOG(0, "");
+ LOG(0, "Shutting down...");
+
+ ConfMan.setBool("last_run", true);
+
+ cleanup();
+
+ delete[] _localSaveDir;
+ delete[] _settingsGameFile;
+ delete[] _savedGameExt;
+
+ delete _cachedThumbnail;
+
+ delete _mathClass;
+
+ delete _transMgr;
+ delete _scEngine;
+ delete _fontStorage;
+ delete _surfaceStorage;
+ delete _videoPlayer;
+ delete _theoraPlayer;
+ delete _soundMgr;
+ //SAFE_DELETE(_keyboardState);
+
+ delete _renderer;
+ delete _stringTable;
+
+ _localSaveDir = NULL;
+ _settingsGameFile = NULL;
+ _savedGameExt = NULL;
+
+ _cachedThumbnail = NULL;
+
+ _mathClass = NULL;
+
+ _transMgr = NULL;
+ _scEngine = NULL;
+ _fontStorage = NULL;
+ _surfaceStorage = NULL;
+ _videoPlayer = NULL;
+ _theoraPlayer = NULL;
+ _soundMgr = NULL;
+
+ _renderer = NULL;
+ _stringTable = NULL;
+
+ DEBUG_DebugDisable();
+ debugC(kWintermuteDebugLog, "--- shutting down normally ---\n");
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::cleanup() {
+ delete _loadingIcon;
+ _loadingIcon = NULL;
+
+ _engineLogCallback = NULL;
+ _engineLogCallbackData = NULL;
+
+ for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) {
+ delete _music[i];
+ _music[i] = NULL;
+ _musicStartTime[i] = 0;
+ }
+
+ unregisterObject(_fader);
+ _fader = NULL;
+
+ for (uint32 i = 0; i < _regObjects.size(); i++) {
+ delete _regObjects[i];
+ _regObjects[i] = NULL;
+ }
+ _regObjects.clear();
+
+ _windows.clear(); // refs only
+ _focusedWindow = NULL; // ref only
+
+ delete _cursorNoninteractive;
+ delete _cursor;
+ delete _activeCursor;
+ _cursorNoninteractive = NULL;
+ _cursor = NULL;
+ _activeCursor = NULL;
+
+ delete _scValue;
+ delete _sFX;
+ _scValue = NULL;
+ _sFX = NULL;
+
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ _scripts[i]->_owner = NULL;
+ _scripts[i]->finish();
+ }
+ _scripts.clear();
+
+ _fontStorage->removeFont(_systemFont);
+ _systemFont = NULL;
+
+ _fontStorage->removeFont(_videoFont);
+ _videoFont = NULL;
+
+ for (uint32 i = 0; i < _quickMessages.size(); i++) {
+ delete _quickMessages[i];
+ }
+ _quickMessages.clear();
+
+ _viewportStack.clear();
+ _viewportSP = -1;
+
+ setName(NULL);
+ setFilename(NULL);
+ for (int i = 0; i < 7; i++) {
+ delete[] _caption[i];
+ _caption[i] = NULL;
+ }
+
+ _lastCursor = NULL;
+
+ delete _keyboardState;
+ _keyboardState = NULL;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseGame::initialize1() {
+ bool loaded = false; // Not really a loop, but a goto-replacement.
+ while (!loaded) {
+ _surfaceStorage = new BaseSurfaceStorage(this);
+ if (_surfaceStorage == NULL) {
+ break;
+ }
+
+ _fontStorage = new BaseFontStorage(this);
+ if (_fontStorage == NULL) {
+ break;
+ }
+
+ _soundMgr = new BaseSoundMgr(this);
+ if (_soundMgr == NULL) {
+ break;
+ }
+
+ _mathClass = new SXMath(this);
+ if (_mathClass == NULL) {
+ break;
+ }
+
+ _scEngine = new ScEngine(this);
+ if (_scEngine == NULL) {
+ break;
+ }
+
+ _videoPlayer = new VideoPlayer(this);
+ if (_videoPlayer == NULL) {
+ break;
+ }
+
+ _transMgr = new BaseTransitionMgr(this);
+ if (_transMgr == NULL) {
+ break;
+ }
+
+ _keyboardState = new BaseKeyboardState(this);
+ if (_keyboardState == NULL) {
+ break;
+ }
+
+ _fader = new BaseFader(this);
+ if (_fader == NULL) {
+ break;
+ }
+ registerObject(_fader);
+
+ loaded = true;
+ }
+ if (loaded == true) {
+ return STATUS_OK;
+ } else {
+ delete _mathClass;
+ delete _keyboardState;
+ delete _transMgr;
+ delete _surfaceStorage;
+ delete _fontStorage;
+ delete _soundMgr;
+ delete _scEngine;
+ delete _videoPlayer;
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseGame::initialize2() { // we know whether we are going to be accelerated
+ _renderer = makeOSystemRenderer(this);
+ if (_renderer == NULL) {
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseGame::initialize3() { // renderer is initialized
+ _posX = _renderer->_width / 2;
+ _posY = _renderer->_height / 2;
+ _renderer->initIndicator();
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseGame::DEBUG_DebugEnable(const char *filename) {
+ _debugDebugMode = true;
+
+ int secs = g_system->getMillis() / 1000;
+ int hours = secs / 3600;
+ secs = secs % 3600;
+ int mins = secs / 60;
+ secs = secs % 60;
+
+#ifdef _DEBUG
+ LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Debug Build) *******************", hours, mins, secs);
+#else
+ LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Release Build) *****************", hours, mins, secs);
+#endif
+
+ LOG(0, "%s ver %d.%d.%d%s, Compiled on " __DATE__ ", " __TIME__, DCGF_NAME, DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, DCGF_VER_SUFFIX);
+
+ AnsiString platform = BasePlatform::getPlatformName();
+ LOG(0, "Platform: %s", platform.c_str());
+ LOG(0, "");
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseGame::DEBUG_DebugDisable() {
+ if (_debugLogFile != NULL) {
+ LOG(0, "********** DEBUG LOG CLOSED ********************************************");
+ //fclose((FILE *)_debugLogFile);
+ _debugLogFile = NULL;
+ }
+ _debugDebugMode = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseGame::LOG(bool res, const char *fmt, ...) {
+ uint32 secs = g_system->getMillis() / 1000;
+ uint32 hours = secs / 3600;
+ secs = secs % 3600;
+ uint32 mins = secs / 60;
+ secs = secs % 60;
+
+ char buff[512];
+ va_list va;
+
+ va_start(va, fmt);
+ vsprintf(buff, fmt, va);
+ va_end(va);
+
+ // redirect to an engine's own callback
+ if (_engineLogCallback) {
+ _engineLogCallback(buff, res, _engineLogCallbackData);
+ }
+
+ debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff);
+
+ //fprintf((FILE *)_debugLogFile, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff);
+ //fflush((FILE *)_debugLogFile);
+
+ //QuickMessage(buff);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::setEngineLogCallback(ENGINE_LOG_CALLBACK callback, void *data) {
+ _engineLogCallback = callback;
+ _engineLogCallbackData = data;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseGame::initLoop() {
+ _viewportSP = -1;
+
+ _currentTime = g_system->getMillis();
+
+ _renderer->initLoop();
+ updateMusicCrossfade();
+
+ _surfaceStorage->initLoop();
+ _fontStorage->initLoop();
+
+
+ //_activeObject = NULL;
+
+ // count FPS
+ _deltaTime = _currentTime - _lastTime;
+ _lastTime = _currentTime;
+ _fpsTime += _deltaTime;
+
+ _liveTimerDelta = _liveTimer - _liveTimerLast;
+ _liveTimerLast = _liveTimer;
+ _liveTimer += MIN((uint32)1000, _deltaTime);
+
+ if (_state != GAME_FROZEN) {
+ _timerDelta = _timer - _timerLast;
+ _timerLast = _timer;
+ _timer += MIN((uint32)1000, _deltaTime);
+ } else {
+ _timerDelta = 0;
+ }
+
+ _framesRendered++;
+ if (_fpsTime > 1000) {
+ _fps = _framesRendered;
+ _framesRendered = 0;
+ _fpsTime = 0;
+ }
+ //_gameRef->LOG(0, "%d", _fps);
+
+ getMousePos(&_mousePos);
+
+ _focusedWindow = NULL;
+ for (int i = _windows.size() - 1; i >= 0; i--) {
+ if (_windows[i]->_visible) {
+ _focusedWindow = _windows[i];
+ break;
+ }
+ }
+
+ updateSounds();
+
+ if (_fader) {
+ _fader->update();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseGame::initInput() {
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseGame::getSequence() {
+ return ++_sequence;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::setOffset(int offsetX, int offsetY) {
+ _offsetX = offsetX;
+ _offsetY = offsetY;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::getOffset(int *offsetX, int *offsetY) {
+ if (offsetX != NULL) {
+ *offsetX = _offsetX;
+ }
+ if (offsetY != NULL) {
+ *offsetY = _offsetY;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "BaseGame::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing GAME file '%s'", filename);
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(GAME)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(NAME)
+TOKEN_DEF(SYSTEM_FONT)
+TOKEN_DEF(VIDEO_FONT)
+TOKEN_DEF(EVENTS)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(ACTIVE_CURSOR)
+TOKEN_DEF(NONINTERACTIVE_CURSOR)
+TOKEN_DEF(STRING_TABLE)
+TOKEN_DEF(RESOLUTION)
+TOKEN_DEF(SETTINGS)
+TOKEN_DEF(REQUIRE_3D_ACCELERATION)
+TOKEN_DEF(REQUIRE_SOUND)
+TOKEN_DEF(HWTL_MODE)
+TOKEN_DEF(ALLOW_WINDOWED_MODE)
+TOKEN_DEF(ALLOW_ACCESSIBILITY_TAB)
+TOKEN_DEF(ALLOW_ABOUT_TAB)
+TOKEN_DEF(ALLOW_ADVANCED)
+TOKEN_DEF(ALLOW_DESKTOP_RES)
+TOKEN_DEF(REGISTRY_PATH)
+TOKEN_DEF(PERSONAL_SAVEGAMES)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(SUBTITLES_SPEED)
+TOKEN_DEF(SUBTITLES)
+TOKEN_DEF(VIDEO_SUBTITLES)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(THUMBNAIL_WIDTH)
+TOKEN_DEF(THUMBNAIL_HEIGHT)
+TOKEN_DEF(INDICATOR_X)
+TOKEN_DEF(INDICATOR_Y)
+TOKEN_DEF(INDICATOR_WIDTH)
+TOKEN_DEF(INDICATOR_HEIGHT)
+TOKEN_DEF(INDICATOR_COLOR)
+TOKEN_DEF(SAVE_IMAGE_X)
+TOKEN_DEF(SAVE_IMAGE_Y)
+TOKEN_DEF(SAVE_IMAGE)
+TOKEN_DEF(LOAD_IMAGE_X)
+TOKEN_DEF(LOAD_IMAGE_Y)
+TOKEN_DEF(LOAD_IMAGE)
+TOKEN_DEF(LOCAL_SAVE_DIR)
+TOKEN_DEF(RICH_SAVED_GAMES)
+TOKEN_DEF(SAVED_GAME_EXT)
+TOKEN_DEF(GUID)
+TOKEN_DEF(COMPAT_KILL_METHOD_THREADS)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(GAME)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(SYSTEM_FONT)
+ TOKEN_TABLE(VIDEO_FONT)
+ TOKEN_TABLE(EVENTS)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(ACTIVE_CURSOR)
+ TOKEN_TABLE(NONINTERACTIVE_CURSOR)
+ TOKEN_TABLE(PERSONAL_SAVEGAMES)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(SUBTITLES_SPEED)
+ TOKEN_TABLE(SUBTITLES)
+ TOKEN_TABLE(VIDEO_SUBTITLES)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(THUMBNAIL_WIDTH)
+ TOKEN_TABLE(THUMBNAIL_HEIGHT)
+ TOKEN_TABLE(INDICATOR_X)
+ TOKEN_TABLE(INDICATOR_Y)
+ TOKEN_TABLE(INDICATOR_WIDTH)
+ TOKEN_TABLE(INDICATOR_HEIGHT)
+ TOKEN_TABLE(INDICATOR_COLOR)
+ TOKEN_TABLE(SAVE_IMAGE_X)
+ TOKEN_TABLE(SAVE_IMAGE_Y)
+ TOKEN_TABLE(SAVE_IMAGE)
+ TOKEN_TABLE(LOAD_IMAGE_X)
+ TOKEN_TABLE(LOAD_IMAGE_Y)
+ TOKEN_TABLE(LOAD_IMAGE)
+ TOKEN_TABLE(LOCAL_SAVE_DIR)
+ TOKEN_TABLE(COMPAT_KILL_METHOD_THREADS)
+ TOKEN_TABLE_END
+
+ // Declare a few variables necessary for moving data from these settings over to the renderer:
+ // The values are the same as the defaults set in BaseRenderer.
+ int loadImageX = 0;
+ int loadImageY = 0;
+ int saveImageX = 0;
+ int saveImageY = 0;
+ int indicatorX = -1;
+ int indicatorY = -1;
+ int indicatorWidth = -1;
+ int indicatorHeight = 8;
+ uint32 indicatorColor = BYTETORGBA(255, 0, 0, 128);
+ Common::String loadImageName = "";
+ Common::String saveImageName = "";
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_GAME) {
+ _gameRef->LOG(0, "'GAME' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_SYSTEM_FONT:
+ if (_systemFont) {
+ _fontStorage->removeFont(_systemFont);
+ }
+ _systemFont = NULL;
+
+ _systemFont = _gameRef->_fontStorage->addFont((char *)params);
+ break;
+
+ case TOKEN_VIDEO_FONT:
+ if (_videoFont) {
+ _fontStorage->removeFont(_videoFont);
+ }
+ _videoFont = NULL;
+
+ _videoFont = _gameRef->_fontStorage->addFont((char *)params);
+ break;
+
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_ACTIVE_CURSOR:
+ delete _activeCursor;
+ _activeCursor = NULL;
+ _activeCursor = new BaseSprite(_gameRef);
+ if (!_activeCursor || DID_FAIL(_activeCursor->loadFile((char *)params))) {
+ delete _activeCursor;
+ _activeCursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NONINTERACTIVE_CURSOR:
+ delete _cursorNoninteractive;
+ _cursorNoninteractive = new BaseSprite(_gameRef);
+ if (!_cursorNoninteractive || DID_FAIL(_cursorNoninteractive->loadFile((char *)params))) {
+ delete _cursorNoninteractive;
+ _cursorNoninteractive = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PERSONAL_SAVEGAMES:
+ parser.scanStr((char *)params, "%b", &_personalizedSave);
+ break;
+
+ case TOKEN_SUBTITLES:
+ parser.scanStr((char *)params, "%b", &_subtitles);
+ break;
+
+ case TOKEN_SUBTITLES_SPEED:
+ parser.scanStr((char *)params, "%d", &_subtitlesSpeed);
+ break;
+
+ case TOKEN_VIDEO_SUBTITLES:
+ parser.scanStr((char *)params, "%b", &_videoSubtitles);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+
+ case TOKEN_THUMBNAIL_WIDTH:
+ parser.scanStr((char *)params, "%d", &_thumbnailWidth);
+ break;
+
+ case TOKEN_THUMBNAIL_HEIGHT:
+ parser.scanStr((char *)params, "%d", &_thumbnailHeight);
+ break;
+
+ case TOKEN_INDICATOR_X:
+ parser.scanStr((char *)params, "%d", &indicatorX);
+ break;
+
+ case TOKEN_INDICATOR_Y:
+ parser.scanStr((char *)params, "%d", &indicatorY);
+ break;
+
+ case TOKEN_INDICATOR_COLOR: {
+ int r, g, b, a;
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &r, &g, &b, &a);
+ indicatorColor = BYTETORGBA(r, g, b, a);
+ }
+ break;
+
+ case TOKEN_INDICATOR_WIDTH:
+ parser.scanStr((char *)params, "%d", &indicatorWidth);
+ break;
+
+ case TOKEN_INDICATOR_HEIGHT:
+ parser.scanStr((char *)params, "%d", &indicatorHeight);
+ break;
+
+ case TOKEN_SAVE_IMAGE:
+ saveImageName = (char *) params;
+ break;
+
+ case TOKEN_SAVE_IMAGE_X:
+ parser.scanStr((char *)params, "%d", &saveImageX);
+ break;
+
+ case TOKEN_SAVE_IMAGE_Y:
+ parser.scanStr((char *)params, "%d", &saveImageY);
+ break;
+
+ case TOKEN_LOAD_IMAGE:
+ loadImageName = (char *) params;
+ break;
+
+ case TOKEN_LOAD_IMAGE_X:
+ parser.scanStr((char *)params, "%d", &loadImageX);
+ break;
+
+ case TOKEN_LOAD_IMAGE_Y:
+ parser.scanStr((char *)params, "%d", &loadImageY);
+ break;
+
+ case TOKEN_LOCAL_SAVE_DIR:
+ BaseUtils::setString(&_localSaveDir, (char *)params);
+ break;
+
+ case TOKEN_COMPAT_KILL_METHOD_THREADS:
+ parser.scanStr((char *)params, "%b", &_compatKillMethodThreads);
+ break;
+ }
+ }
+
+ _renderer->setIndicator(indicatorWidth, indicatorHeight, indicatorX, indicatorY, indicatorColor);
+ _renderer->initIndicator(); // In case we just reset the values.
+ _renderer->setSaveImage(saveImageName.c_str(), saveImageX, saveImageY);
+ _renderer->setLoadingScreen(loadImageName.c_str(), loadImageX, loadImageY);
+
+ if (!_systemFont) {
+ _systemFont = _gameRef->_fontStorage->addFont("system_font.fnt");
+ }
+
+
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in GAME definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading GAME definition");
+ return STATUS_FAILED;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // LOG
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "LOG") == 0) {
+ stack->correctParams(1);
+ LOG(0, stack->pop()->getString());
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Caption
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Caption") == 0) {
+ bool res = BaseObject::scCallMethod(script, stack, thisStack, name);
+ setWindowTitle();
+ return res;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Msg
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Msg") == 0) {
+ stack->correctParams(1);
+ quickMessage(stack->pop()->getString());
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RunScript
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RunScript") == 0) {
+ _gameRef->LOG(0, "**Warning** The 'RunScript' method is now obsolete. Use 'AttachScript' instead (same syntax)");
+ stack->correctParams(1);
+ if (DID_FAIL(addScript(stack->pop()->getString()))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadStringTable
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadStringTable") == 0) {
+ stack->correctParams(2);
+ const char *filename = stack->pop()->getString();
+ ScValue *val = stack->pop();
+
+ bool clearOld;
+ if (val->isNULL()) {
+ clearOld = true;
+ } else {
+ clearOld = val->getBool();
+ }
+
+ if (DID_FAIL(_stringTable->loadFile(filename, clearOld))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ValidObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ValidObject") == 0) {
+ stack->correctParams(1);
+ BaseScriptable *obj = stack->pop()->getNative();
+ if (validObject((BaseObject *) obj)) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Reset
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Reset") == 0) {
+ stack->correctParams(0);
+ resetContent();
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // UnloadObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "UnloadObject") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ BaseObject *obj = (BaseObject *)val->getNative();
+ unregisterObject(obj);
+ if (val->getType() == VAL_VARIABLE_REF) {
+ val->setNULL();
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadWindow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadWindow") == 0) {
+ stack->correctParams(1);
+ UIWindow *win = new UIWindow(_gameRef);
+ if (win && DID_SUCCEED(win->loadFile(stack->pop()->getString()))) {
+ _windows.add(win);
+ registerObject(win);
+ stack->pushNative(win, true);
+ } else {
+ delete win;
+ win = NULL;
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ExpandString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ExpandString") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ char *str = new char[strlen(val->getString()) + 1];
+ strcpy(str, val->getString());
+ _stringTable->expand(&str);
+ stack->pushString(str);
+ delete[] str;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PlayMusic / PlayMusicChannel
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PlayMusic") == 0 || strcmp(name, "PlayMusicChannel") == 0) {
+ int channel = 0;
+ if (strcmp(name, "PlayMusic") == 0) {
+ stack->correctParams(3);
+ } else {
+ stack->correctParams(4);
+ channel = stack->pop()->getInt();
+ }
+
+ const char *filename = stack->pop()->getString();
+ ScValue *valLooping = stack->pop();
+ bool looping = valLooping->isNULL() ? true : valLooping->getBool();
+
+ ScValue *valLoopStart = stack->pop();
+ uint32 loopStart = (uint32)(valLoopStart->isNULL() ? 0 : valLoopStart->getInt());
+
+
+ if (DID_FAIL(playMusic(channel, filename, looping, loopStart))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // StopMusic / StopMusicChannel
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "StopMusic") == 0 || strcmp(name, "StopMusicChannel") == 0) {
+ int channel = 0;
+
+ if (strcmp(name, "StopMusic") == 0) {
+ stack->correctParams(0);
+ } else {
+ stack->correctParams(1);
+ channel = stack->pop()->getInt();
+ }
+
+ if (DID_FAIL(stopMusic(channel))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PauseMusic / PauseMusicChannel
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PauseMusic") == 0 || strcmp(name, "PauseMusicChannel") == 0) {
+ int channel = 0;
+
+ if (strcmp(name, "PauseMusic") == 0) {
+ stack->correctParams(0);
+ } else {
+ stack->correctParams(1);
+ channel = stack->pop()->getInt();
+ }
+
+ if (DID_FAIL(pauseMusic(channel))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ResumeMusic / ResumeMusicChannel
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ResumeMusic") == 0 || strcmp(name, "ResumeMusicChannel") == 0) {
+ int channel = 0;
+ if (strcmp(name, "ResumeMusic") == 0) {
+ stack->correctParams(0);
+ } else {
+ stack->correctParams(1);
+ channel = stack->pop()->getInt();
+ }
+
+ if (DID_FAIL(resumeMusic(channel))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetMusic / GetMusicChannel
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetMusic") == 0 || strcmp(name, "GetMusicChannel") == 0) {
+ int channel = 0;
+ if (strcmp(name, "GetMusic") == 0) {
+ stack->correctParams(0);
+ } else {
+ stack->correctParams(1);
+ channel = stack->pop()->getInt();
+ }
+ if (channel < 0 || channel >= NUM_MUSIC_CHANNELS) {
+ stack->pushNULL();
+ } else {
+ if (!_music[channel] || !_music[channel]->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_music[channel]->getFilename());
+ }
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetMusicPosition / SetMusicChannelPosition
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetMusicPosition") == 0 || strcmp(name, "SetMusicChannelPosition") == 0 || strcmp(name, "SetMusicPositionChannel") == 0) {
+ int channel = 0;
+ if (strcmp(name, "SetMusicPosition") == 0) {
+ stack->correctParams(1);
+ } else {
+ stack->correctParams(2);
+ channel = stack->pop()->getInt();
+ }
+
+ uint32 time = stack->pop()->getInt();
+
+ if (DID_FAIL(setMusicStartTime(channel, time))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetMusicPosition / GetMusicChannelPosition
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetMusicPosition") == 0 || strcmp(name, "GetMusicChannelPosition") == 0) {
+ int channel = 0;
+ if (strcmp(name, "GetMusicPosition") == 0) {
+ stack->correctParams(0);
+ } else {
+ stack->correctParams(1);
+ channel = stack->pop()->getInt();
+ }
+
+ if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) {
+ stack->pushInt(0);
+ } else {
+ stack->pushInt(_music[channel]->getPositionTime());
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsMusicPlaying / IsMusicChannelPlaying
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsMusicPlaying") == 0 || strcmp(name, "IsMusicChannelPlaying") == 0) {
+ int channel = 0;
+ if (strcmp(name, "IsMusicPlaying") == 0) {
+ stack->correctParams(0);
+ } else {
+ stack->correctParams(1);
+ channel = stack->pop()->getInt();
+ }
+
+ if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(_music[channel]->isPlaying());
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetMusicVolume / SetMusicChannelVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetMusicVolume") == 0 || strcmp(name, "SetMusicChannelVolume") == 0) {
+ int channel = 0;
+ if (strcmp(name, "SetMusicVolume") == 0) {
+ stack->correctParams(1);
+ } else {
+ stack->correctParams(2);
+ channel = stack->pop()->getInt();
+ }
+
+ int volume = stack->pop()->getInt();
+ if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) {
+ stack->pushBool(false);
+ } else {
+ if (DID_FAIL(_music[channel]->setVolumePercent(volume))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetMusicVolume / GetMusicChannelVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetMusicVolume") == 0 || strcmp(name, "GetMusicChannelVolume") == 0) {
+ int channel = 0;
+ if (strcmp(name, "GetMusicVolume") == 0) {
+ stack->correctParams(0);
+ } else {
+ stack->correctParams(1);
+ channel = stack->pop()->getInt();
+ }
+
+ if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) {
+ stack->pushInt(0);
+ } else {
+ stack->pushInt(_music[channel]->getVolumePercent());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MusicCrossfade
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MusicCrossfade") == 0) {
+ stack->correctParams(4);
+ int channel1 = stack->pop()->getInt(0);
+ int channel2 = stack->pop()->getInt(0);
+ uint32 fadeLength = (uint32)stack->pop()->getInt(0);
+ bool swap = stack->pop()->getBool(true);
+
+ if (_musicCrossfadeRunning) {
+ script->runtimeError("Game.MusicCrossfade: Music crossfade is already in progress.");
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ _musicCrossfadeStartTime = _liveTimer;
+ _musicCrossfadeChannel1 = channel1;
+ _musicCrossfadeChannel2 = channel2;
+ _musicCrossfadeLength = fadeLength;
+ _musicCrossfadeSwap = swap;
+
+ _musicCrossfadeRunning = true;
+
+ stack->pushBool(true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSoundLength
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSoundLength") == 0) {
+ stack->correctParams(1);
+
+ int length = 0;
+ const char *filename = stack->pop()->getString();
+
+ BaseSound *sound = new BaseSound(_gameRef);
+ if (sound && DID_SUCCEED(sound->setSound(filename, Audio::Mixer::kMusicSoundType, true))) {
+ length = sound->getLength();
+ delete sound;
+ sound = NULL;
+ }
+ stack->pushInt(length);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetMousePos
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetMousePos") == 0) {
+ stack->correctParams(2);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+ x = MAX(x, 0);
+ x = MIN(x, _renderer->_width);
+ y = MAX(y, 0);
+ y = MIN(y, _renderer->_height);
+ Point32 p;
+ p.x = x + _renderer->_drawOffsetX;
+ p.y = y + _renderer->_drawOffsetY;
+
+ BasePlatform::setCursorPos(p.x, p.y);
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LockMouseRect
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LockMouseRect") == 0) {
+ stack->correctParams(4);
+ int left = stack->pop()->getInt();
+ int top = stack->pop()->getInt();
+ int right = stack->pop()->getInt();
+ int bottom = stack->pop()->getInt();
+
+ if (right < left) {
+ BaseUtils::swap(&left, &right);
+ }
+ if (bottom < top) {
+ BaseUtils::swap(&top, &bottom);
+ }
+
+ BasePlatform::setRect(&_mouseLockRect, left, top, right, bottom);
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PlayVideo
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PlayVideo") == 0) {
+ _gameRef->LOG(0, "Warning: Game.PlayVideo() is now deprecated. Use Game.PlayTheora() instead.");
+
+ stack->correctParams(6);
+ const char *filename = stack->pop()->getString();
+ warning("PlayVideo: %s - not implemented yet", filename);
+ ScValue *valType = stack->pop();
+ int type;
+ if (valType->isNULL()) {
+ type = (int)VID_PLAY_STRETCH;
+ } else {
+ type = valType->getInt();
+ }
+
+ int xVal = stack->pop()->getInt();
+ int yVal = stack->pop()->getInt();
+ bool freezeMusic = stack->pop()->getBool(true);
+
+ ScValue *valSub = stack->pop();
+ const char *subtitleFile = valSub->isNULL() ? NULL : valSub->getString();
+
+ if (type < (int)VID_PLAY_POS || type > (int)VID_PLAY_CENTER) {
+ type = (int)VID_PLAY_STRETCH;
+ }
+
+ if (DID_SUCCEED(_gameRef->_videoPlayer->initialize(filename, subtitleFile))) {
+ if (DID_SUCCEED(_gameRef->_videoPlayer->play((TVideoPlayback)type, xVal, yVal, freezeMusic))) {
+ stack->pushBool(true);
+ script->sleep(0);
+ } else {
+ stack->pushBool(false);
+ }
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PlayTheora
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PlayTheora") == 0) {
+ stack->correctParams(7);
+ const char *filename = stack->pop()->getString();
+ ScValue *valType = stack->pop();
+ int type;
+ if (valType->isNULL()) {
+ type = (int)VID_PLAY_STRETCH;
+ } else {
+ type = valType->getInt();
+ }
+
+ int xVal = stack->pop()->getInt();
+ int yVal = stack->pop()->getInt();
+ bool freezeMusic = stack->pop()->getBool(true);
+ bool dropFrames = stack->pop()->getBool(true);
+
+ ScValue *valSub = stack->pop();
+ const char *subtitleFile = valSub->isNULL() ? NULL : valSub->getString();
+
+ if (type < (int)VID_PLAY_POS || type > (int)VID_PLAY_CENTER) {
+ type = (int)VID_PLAY_STRETCH;
+ }
+
+ delete _theoraPlayer;
+ _theoraPlayer = new VideoTheoraPlayer(this);
+ if (_theoraPlayer && DID_SUCCEED(_theoraPlayer->initialize(filename, subtitleFile))) {
+ _theoraPlayer->_dontDropFrames = !dropFrames;
+ if (DID_SUCCEED(_theoraPlayer->play((TVideoPlayback)type, xVal, yVal, true, freezeMusic))) {
+ stack->pushBool(true);
+ script->sleep(0);
+ } else {
+ stack->pushBool(false);
+ }
+ } else {
+ stack->pushBool(false);
+ delete _theoraPlayer;
+ _theoraPlayer = NULL;
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // QuitGame
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "QuitGame") == 0) {
+ stack->correctParams(0);
+ stack->pushNULL();
+ _quitting = true;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RegWriteNumber
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RegWriteNumber") == 0) {
+ stack->correctParams(2);
+ const char *key = stack->pop()->getString();
+ int val = stack->pop()->getInt();
+ Common::String privKey = "priv_" + StringUtil::encodeSetting(key);
+ ConfMan.setInt(privKey, val);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RegReadNumber
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RegReadNumber") == 0) {
+ stack->correctParams(2);
+ const char *key = stack->pop()->getString();
+ int initVal = stack->pop()->getInt();
+ Common::String privKey = "priv_" + StringUtil::encodeSetting(key);
+ int result = initVal;
+ if (ConfMan.hasKey(privKey)) {
+ result = ConfMan.getInt(privKey);
+ }
+ stack->pushInt(result);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RegWriteString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RegWriteString") == 0) {
+ stack->correctParams(2);
+ const char *key = stack->pop()->getString();
+ const char *val = stack->pop()->getString();
+ Common::String privKey = "priv_" + StringUtil::encodeSetting(key);
+ Common::String privVal = StringUtil::encodeSetting(val);
+ ConfMan.set(privKey, privVal);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RegReadString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RegReadString") == 0) {
+ stack->correctParams(2);
+ const char *key = stack->pop()->getString();
+ const char *initVal = stack->pop()->getString();
+ Common::String privKey = "priv_" + StringUtil::encodeSetting(key);
+ Common::String result = initVal;
+ if (ConfMan.hasKey(privKey)) {
+ result = StringUtil::decodeSetting(ConfMan.get(key));
+ }
+ stack->pushString(result.c_str());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SaveGame
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SaveGame") == 0) {
+ stack->correctParams(3);
+ int slot = stack->pop()->getInt();
+ const char *xdesc = stack->pop()->getString();
+ bool quick = stack->pop()->getBool(false);
+
+ char *desc = new char[strlen(xdesc) + 1];
+ strcpy(desc, xdesc);
+ stack->pushBool(true);
+ if (DID_FAIL(saveGame(slot, desc, quick))) {
+ stack->pop();
+ stack->pushBool(false);
+ }
+ delete[] desc;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadGame
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadGame") == 0) {
+ stack->correctParams(1);
+ _scheduledLoadSlot = stack->pop()->getInt();
+ _loading = true;
+ stack->pushBool(false);
+ script->sleep(0);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsSaveSlotUsed
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsSaveSlotUsed") == 0) {
+ stack->correctParams(1);
+ int slot = stack->pop()->getInt();
+ stack->pushBool(SaveLoad::isSaveSlotUsed(slot));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSaveSlotDescription
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSaveSlotDescription") == 0) {
+ stack->correctParams(1);
+ int slot = stack->pop()->getInt();
+ char desc[512];
+ desc[0] = '\0';
+ SaveLoad::getSaveSlotDescription(slot, desc);
+ stack->pushString(desc);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // EmptySaveSlot
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "EmptySaveSlot") == 0) {
+ stack->correctParams(1);
+ int slot = stack->pop()->getInt();
+ SaveLoad::emptySaveSlot(slot);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetGlobalSFXVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetGlobalSFXVolume") == 0) {
+ stack->correctParams(1);
+ _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSFXSoundType, (byte)stack->pop()->getInt());
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetGlobalSpeechVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetGlobalSpeechVolume") == 0) {
+ stack->correctParams(1);
+ _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSpeechSoundType, (byte)stack->pop()->getInt());
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetGlobalMusicVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetGlobalMusicVolume") == 0) {
+ stack->correctParams(1);
+ _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kMusicSoundType, (byte)stack->pop()->getInt());
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetGlobalMasterVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetGlobalMasterVolume") == 0) {
+ stack->correctParams(1);
+ _gameRef->_soundMgr->setMasterVolumePercent((byte)stack->pop()->getInt());
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetGlobalSFXVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetGlobalSFXVolume") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetGlobalSpeechVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetGlobalSpeechVolume") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetGlobalMusicVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetGlobalMusicVolume") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetGlobalMasterVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetGlobalMasterVolume") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_soundMgr->getMasterVolumePercent());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetActiveCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetActiveCursor") == 0) {
+ stack->correctParams(1);
+ if (DID_SUCCEED(setActiveCursor(stack->pop()->getString()))) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetActiveCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetActiveCursor") == 0) {
+ stack->correctParams(0);
+ if (!_activeCursor || !_activeCursor->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_activeCursor->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetActiveCursorObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetActiveCursorObject") == 0) {
+ stack->correctParams(0);
+ if (!_activeCursor) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_activeCursor, true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RemoveActiveCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemoveActiveCursor") == 0) {
+ stack->correctParams(0);
+ delete _activeCursor;
+ _activeCursor = NULL;
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HasActiveCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HasActiveCursor") == 0) {
+ stack->correctParams(0);
+
+ if (_activeCursor) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FileExists
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FileExists") == 0) {
+ stack->correctParams(1);
+ const char *filename = stack->pop()->getString();
+
+ bool exists = BaseFileManager::getEngineInstance()->hasFile(filename); // Had absPathWarning = false
+ stack->pushBool(exists);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FadeOut / FadeOutAsync / SystemFadeOut / SystemFadeOutAsync
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FadeOut") == 0 || strcmp(name, "FadeOutAsync") == 0 || strcmp(name, "SystemFadeOut") == 0 || strcmp(name, "SystemFadeOutAsync") == 0) {
+ stack->correctParams(5);
+ uint32 duration = stack->pop()->getInt(500);
+ byte red = stack->pop()->getInt(0);
+ byte green = stack->pop()->getInt(0);
+ byte blue = stack->pop()->getInt(0);
+ byte alpha = stack->pop()->getInt(0xFF);
+
+ bool system = (strcmp(name, "SystemFadeOut") == 0 || strcmp(name, "SystemFadeOutAsync") == 0);
+
+ _fader->fadeOut(BYTETORGBA(red, green, blue, alpha), duration, system);
+ if (strcmp(name, "FadeOutAsync") != 0 && strcmp(name, "SystemFadeOutAsync") != 0) {
+ script->waitFor(_fader);
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FadeIn / FadeInAsync / SystemFadeIn / SystemFadeInAsync
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FadeIn") == 0 || strcmp(name, "FadeInAsync") == 0 || strcmp(name, "SystemFadeIn") == 0 || strcmp(name, "SystemFadeInAsync") == 0) {
+ stack->correctParams(5);
+ uint32 duration = stack->pop()->getInt(500);
+ byte red = stack->pop()->getInt(0);
+ byte green = stack->pop()->getInt(0);
+ byte blue = stack->pop()->getInt(0);
+ byte alpha = stack->pop()->getInt(0xFF);
+
+ bool system = (strcmp(name, "SystemFadeIn") == 0 || strcmp(name, "SystemFadeInAsync") == 0);
+
+ _fader->fadeIn(BYTETORGBA(red, green, blue, alpha), duration, system);
+ if (strcmp(name, "FadeInAsync") != 0 && strcmp(name, "SystemFadeInAsync") != 0) {
+ script->waitFor(_fader);
+ }
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFadeColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFadeColor") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_fader->getCurrentColor());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Screenshot
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Screenshot") == 0) {
+ stack->correctParams(1);
+ char filename[MAX_PATH_LENGTH];
+
+ ScValue *val = stack->pop();
+
+ warning("BGame::ScCallMethod - Screenshot not reimplemented"); //TODO
+ int fileNum = 0;
+
+ while (true) {
+ sprintf(filename, "%s%03d.bmp", val->isNULL() ? getName() : val->getString(), fileNum);
+ if (!Common::File::exists(filename)) {
+ break;
+ }
+ fileNum++;
+ }
+
+ bool ret = false;
+ BaseImage *image = _gameRef->_renderer->takeScreenshot();
+ if (image) {
+ ret = DID_SUCCEED(image->saveBMPFile(filename));
+ delete image;
+ } else {
+ ret = false;
+ }
+
+ stack->pushBool(ret);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScreenshotEx
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScreenshotEx") == 0) {
+ stack->correctParams(3);
+ const char *filename = stack->pop()->getString();
+ int sizeX = stack->pop()->getInt(_renderer->_width);
+ int sizeY = stack->pop()->getInt(_renderer->_height);
+
+ bool ret = false;
+ BaseImage *image = _gameRef->_renderer->takeScreenshot();
+ if (image) {
+ ret = DID_SUCCEED(image->resize(sizeX, sizeY));
+ if (ret) {
+ ret = DID_SUCCEED(image->saveBMPFile(filename));
+ }
+ delete image;
+ } else {
+ ret = false;
+ }
+
+ stack->pushBool(ret);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateWindow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateWindow") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ UIWindow *win = new UIWindow(_gameRef);
+ _windows.add(win);
+ registerObject(win);
+ if (!val->isNULL()) {
+ win->setName(val->getString());
+ }
+ stack->pushNative(win, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteWindow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteWindow") == 0) {
+ stack->correctParams(1);
+ BaseObject *obj = (BaseObject *)stack->pop()->getNative();
+ for (uint32 i = 0; i < _windows.size(); i++) {
+ if (_windows[i] == obj) {
+ unregisterObject(_windows[i]);
+ stack->pushBool(true);
+ return STATUS_OK;
+ }
+ }
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // OpenDocument
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "OpenDocument") == 0) {
+ stack->correctParams(0);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DEBUG_DumpClassRegistry
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DEBUG_DumpClassRegistry") == 0) {
+ stack->correctParams(0);
+ DEBUG_DumpClassRegistry();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetLoadingScreen
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetLoadingScreen") == 0) {
+ stack->correctParams(3);
+ ScValue *val = stack->pop();
+ int loadImageX = stack->pop()->getInt();
+ int loadImageY = stack->pop()->getInt();
+
+ if (val->isNULL()) {
+ _renderer->setLoadingScreen(NULL, loadImageX, loadImageY);
+ } else {
+ _renderer->setLoadingScreen(val->getString(), loadImageX, loadImageY);
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetSavingScreen
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetSavingScreen") == 0) {
+ stack->correctParams(3);
+ ScValue *val = stack->pop();
+ int saveImageX = stack->pop()->getInt();
+ int saveImageY = stack->pop()->getInt();
+
+ if (val->isNULL()) {
+ _renderer->setSaveImage(NULL, saveImageX, saveImageY);
+ } else {
+ _renderer->setSaveImage(NULL, saveImageX, saveImageY);
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetWaitCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetWaitCursor") == 0) {
+ stack->correctParams(1);
+ if (DID_SUCCEED(setWaitCursor(stack->pop()->getString()))) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RemoveWaitCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemoveWaitCursor") == 0) {
+ stack->correctParams(0);
+ delete _cursorNoninteractive;
+ _cursorNoninteractive = NULL;
+
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetWaitCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetWaitCursor") == 0) {
+ stack->correctParams(0);
+ if (!_cursorNoninteractive || !_cursorNoninteractive->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_cursorNoninteractive->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetWaitCursorObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetWaitCursorObject") == 0) {
+ stack->correctParams(0);
+ if (!_cursorNoninteractive) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_cursorNoninteractive, true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ClearScriptCache
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ClearScriptCache") == 0) {
+ stack->correctParams(0);
+ stack->pushBool(DID_SUCCEED(_scEngine->emptyScriptCache()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DisplayLoadingIcon
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DisplayLoadingIcon") == 0) {
+ stack->correctParams(4);
+
+ const char *filename = stack->pop()->getString();
+ _loadingIconX = stack->pop()->getInt();
+ _loadingIconY = stack->pop()->getInt();
+ _loadingIconPersistent = stack->pop()->getBool();
+
+ delete _loadingIcon;
+ _loadingIcon = new BaseSprite(this);
+ if (!_loadingIcon || DID_FAIL(_loadingIcon->loadFile(filename))) {
+ delete _loadingIcon;
+ _loadingIcon = NULL;
+ } else {
+ displayContent(false, true);
+ _gameRef->_renderer->flip();
+ _gameRef->_renderer->initLoop();
+ }
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HideLoadingIcon
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HideLoadingIcon") == 0) {
+ stack->correctParams(0);
+ delete _loadingIcon;
+ _loadingIcon = NULL;
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DumpTextureStats
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DumpTextureStats") == 0) {
+ stack->correctParams(1);
+ const char *filename = stack->pop()->getString();
+
+ _renderer->dumpData(filename);
+
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccOutputText
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AccOutputText") == 0) {
+ stack->correctParams(2);
+ /* const char *str = */ stack->pop()->getString();
+ /* int type = */ stack->pop()->getInt();
+ // do nothing
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // StoreSaveThumbnail
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "StoreSaveThumbnail") == 0) {
+ stack->correctParams(0);
+ delete _cachedThumbnail;
+ _cachedThumbnail = new BaseSaveThumbHelper(this);
+ if (DID_FAIL(_cachedThumbnail->storeThumbnail())) {
+ delete _cachedThumbnail;
+ _cachedThumbnail = NULL;
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteSaveThumbnail
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteSaveThumbnail") == 0) {
+ stack->correctParams(0);
+ delete _cachedThumbnail;
+ _cachedThumbnail = NULL;
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFileChecksum
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFileChecksum") == 0) {
+ stack->correctParams(2);
+ const char *filename = stack->pop()->getString();
+ bool asHex = stack->pop()->getBool(false);
+
+ Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(filename, false);
+ if (file) {
+ crc remainder = crc_initialize();
+ byte buf[1024];
+ int bytesRead = 0;
+
+ while (bytesRead < file->size()) {
+ int bufSize = MIN((uint32)1024, (uint32)(file->size() - bytesRead));
+ bytesRead += file->read(buf, bufSize);
+
+ for (int i = 0; i < bufSize; i++) {
+ remainder = crc_process_byte(buf[i], remainder);
+ }
+ }
+ crc checksum = crc_finalize(remainder);
+
+ if (asHex) {
+ char hex[100];
+ sprintf(hex, "%x", checksum);
+ stack->pushString(hex);
+ } else {
+ stack->pushInt(checksum);
+ }
+
+ BaseFileManager::getEngineInstance()->closeFile(file);
+ file = NULL;
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // EnableScriptProfiling
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "EnableScriptProfiling") == 0) {
+ stack->correctParams(0);
+ _scEngine->enableProfiling();
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DisableScriptProfiling
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DisableScriptProfiling") == 0) {
+ stack->correctParams(0);
+ _scEngine->disableProfiling();
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ShowStatusLine
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ShowStatusLine") == 0) {
+ stack->correctParams(0);
+ // Block kept to show intention of opcode.
+ /*#ifdef __IPHONEOS__
+ IOS_ShowStatusLine(TRUE);
+ #endif*/
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HideStatusLine
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HideStatusLine") == 0) {
+ stack->correctParams(0);
+ // Block kept to show intention of opcode.
+ /*#ifdef __IPHONEOS__
+ IOS_ShowStatusLine(FALSE);
+ #endif*/
+ stack->pushNULL();
+
+ return STATUS_OK;
+ } else {
+ return BaseObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseGame::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("game");
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Name") {
+ _scValue->setString(getName());
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Hwnd (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Hwnd") {
+ _scValue->setInt((int)_renderer->_window);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CurrentTime (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "CurrentTime") {
+ _scValue->setInt((int)_timer);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WindowsTime (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "WindowsTime") {
+ _scValue->setInt((int)g_system->getMillis());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WindowedMode (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "WindowedMode") {
+ _scValue->setBool(_renderer->_windowed);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MouseX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MouseX") {
+ _scValue->setInt(_mousePos.x);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MouseY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MouseY") {
+ _scValue->setInt(_mousePos.y);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MainObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MainObject") {
+ _scValue->setNative(_mainObject, true);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ActiveObject (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ActiveObject") {
+ _scValue->setNative(_activeObject, true);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScreenWidth (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScreenWidth") {
+ _scValue->setInt(_renderer->_width);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScreenHeight (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScreenHeight") {
+ _scValue->setInt(_renderer->_height);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Interactive
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Interactive") {
+ _scValue->setBool(_interactive);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DebugMode (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "DebugMode") {
+ _scValue->setBool(_debugDebugMode);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SoundAvailable (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SoundAvailable") {
+ _scValue->setBool(_soundMgr->_soundAvailable);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SFXVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SFXVolume") {
+ _gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete");
+ _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType));
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SpeechVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SpeechVolume") {
+ _gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete");
+ _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType));
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MusicVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MusicVolume") {
+ _gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete");
+ _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType));
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MasterVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MasterVolume") {
+ _gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete");
+ _scValue->setInt(_soundMgr->getMasterVolumePercent());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Keyboard (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Keyboard") {
+ if (_keyboardState) {
+ _scValue->setNative(_keyboardState, true);
+ } else {
+ _scValue->setNULL();
+ }
+
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Subtitles
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Subtitles") {
+ _scValue->setBool(_subtitles);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesSpeed
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SubtitlesSpeed") {
+ _scValue->setInt(_subtitlesSpeed);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // VideoSubtitles
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "VideoSubtitles") {
+ _scValue->setBool(_videoSubtitles);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FPS (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "FPS") {
+ _scValue->setInt(_fps);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AcceleratedMode / Accelerated (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AcceleratedMode" || name == "Accelerated") {
+ _scValue->setBool(_useD3D);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TextEncoding
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TextEncoding") {
+ _scValue->setInt(_textEncoding);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TextRTL
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TextRTL") {
+ _scValue->setBool(_textRTL);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SoundBufferSize
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SoundBufferSize") {
+ _scValue->setInt(_soundBufferSizeSec);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SuspendedRendering
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SuspendedRendering") {
+ _scValue->setBool(_suspendedRendering);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SuppressScriptErrors
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SuppressScriptErrors") {
+ _scValue->setBool(_suppressScriptErrors);
+ return _scValue;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Frozen
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Frozen") {
+ _scValue->setBool(_state == GAME_FROZEN);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccTTSEnabled
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccTTSEnabled") {
+ _scValue->setBool(false);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccTTSTalk
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccTTSTalk") {
+ _scValue->setBool(false);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccTTSCaptions
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccTTSCaptions") {
+ _scValue->setBool(false);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccTTSKeypress
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccTTSKeypress") {
+ _scValue->setBool(false);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccKeyboardEnabled
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccKeyboardEnabled") {
+ _scValue->setBool(false);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccKeyboardCursorSkip
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccKeyboardCursorSkip") {
+ _scValue->setBool(false);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccKeyboardPause
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccKeyboardPause") {
+ _scValue->setBool(false);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AutorunDisabled
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AutorunDisabled") {
+ _scValue->setBool(_autorunDisabled);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SaveDirectory (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SaveDirectory") {
+ AnsiString dataDir = "saves/"; // TODO: This is just to avoid telling the engine actual paths.
+ _scValue->setString(dataDir.c_str());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AutoSaveOnExit
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AutoSaveOnExit") {
+ _scValue->setBool(_autoSaveOnExit);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AutoSaveSlot
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AutoSaveSlot") {
+ _scValue->setInt(_autoSaveSlot);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CursorHidden
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "CursorHidden") {
+ _scValue->setBool(_cursorHidden);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Platform (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Platform") {
+ _scValue->setString(BasePlatform::getPlatformName().c_str());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeviceType (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "DeviceType") {
+ _scValue->setString(getDeviceType().c_str());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MostRecentSaveSlot (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MostRecentSaveSlot") {
+ if (!ConfMan.hasKey("most_recent_saveslot")) {
+ _scValue->setInt(-1);
+ } else {
+ _scValue->setInt(ConfMan.getInt("most_recent_saveslot"));
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Store (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Store") {
+ _scValue->setNULL();
+ error("Request for a SXStore-object, which is not supported by ScummVM");
+
+ return _scValue;
+ } else {
+ return BaseObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MouseX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MouseX") == 0) {
+ _mousePos.x = value->getInt();
+ resetMousePos();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MouseY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MouseY") == 0) {
+ _mousePos.y = value->getInt();
+ resetMousePos();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Caption
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Name") == 0) {
+ bool res = BaseObject::scSetProperty(name, value);
+ setWindowTitle();
+ return res;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MainObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MainObject") == 0) {
+ BaseScriptable *obj = value->getNative();
+ if (obj == NULL || validObject((BaseObject *)obj)) {
+ _mainObject = (BaseObject *)obj;
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Interactive
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Interactive") == 0) {
+ setInteractive(value->getBool());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SFXVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SFXVolume") == 0) {
+ _gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete");
+ _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSFXSoundType, (byte)value->getInt());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SpeechVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SpeechVolume") == 0) {
+ _gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete");
+ _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSpeechSoundType, (byte)value->getInt());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MusicVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MusicVolume") == 0) {
+ _gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete");
+ _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kMusicSoundType, (byte)value->getInt());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MasterVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MasterVolume") == 0) {
+ _gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete");
+ _gameRef->_soundMgr->setMasterVolumePercent((byte)value->getInt());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Subtitles
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Subtitles") == 0) {
+ _subtitles = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SubtitlesSpeed
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SubtitlesSpeed") == 0) {
+ _subtitlesSpeed = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // VideoSubtitles
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "VideoSubtitles") == 0) {
+ _videoSubtitles = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TextEncoding
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TextEncoding") == 0) {
+ int enc = value->getInt();
+ if (enc < 0) {
+ enc = 0;
+ }
+ if (enc >= NUM_TEXT_ENCODINGS) {
+ enc = NUM_TEXT_ENCODINGS - 1;
+ }
+ _textEncoding = (TTextEncoding)enc;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TextRTL
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TextRTL") == 0) {
+ _textRTL = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SoundBufferSize
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SoundBufferSize") == 0) {
+ _soundBufferSizeSec = value->getInt();
+ _soundBufferSizeSec = MAX(3, _soundBufferSizeSec);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SuspendedRendering
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SuspendedRendering") == 0) {
+ _suspendedRendering = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SuppressScriptErrors
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SuppressScriptErrors") == 0) {
+ _suppressScriptErrors = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AutorunDisabled
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AutorunDisabled") == 0) {
+ _autorunDisabled = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AutoSaveOnExit
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AutoSaveOnExit") == 0) {
+ _autoSaveOnExit = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AutoSaveSlot
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AutoSaveSlot") == 0) {
+ _autoSaveSlot = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CursorHidden
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CursorHidden") == 0) {
+ _cursorHidden = value->getBool();
+ return STATUS_OK;
+ } else {
+ return BaseObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseGame::scToString() {
+ return "[game object]";
+}
+
+
+
+#define QUICK_MSG_DURATION 3000
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::displayQuickMsg() {
+ if (_quickMessages.size() == 0 || !_systemFont) {
+ return STATUS_OK;
+ }
+
+ // update
+ for (uint32 i = 0; i < _quickMessages.size(); i++) {
+ if (_currentTime - _quickMessages[i]->_startTime >= QUICK_MSG_DURATION) {
+ delete _quickMessages[i];
+ _quickMessages.remove_at(i);
+ i--;
+ }
+ }
+
+ int posY = 20;
+
+ // display
+ for (uint32 i = 0; i < _quickMessages.size(); i++) {
+ _systemFont->drawText((byte *)_quickMessages[i]->getText(), 0, posY, _renderer->_width);
+ posY += _systemFont->getTextHeight((byte *)_quickMessages[i]->getText(), _renderer->_width);
+ }
+ return STATUS_OK;
+}
+
+
+#define MAX_QUICK_MSG 5
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::quickMessage(const char *text) {
+ if (_quickMessages.size() >= MAX_QUICK_MSG) {
+ delete _quickMessages[0];
+ _quickMessages.remove_at(0);
+ }
+ _quickMessages.add(new BaseQuickMsg(_gameRef, text));
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::quickMessageForm(char *fmt, ...) {
+ char buff[256];
+ va_list va;
+
+ va_start(va, fmt);
+ vsprintf(buff, fmt, va);
+ va_end(va);
+
+ quickMessage(buff);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::registerObject(BaseObject *object) {
+ _regObjects.add(object);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::unregisterObject(BaseObject *object) {
+ if (!object) {
+ return STATUS_OK;
+ }
+
+ // is it a window?
+ for (uint32 i = 0; i < _windows.size(); i++) {
+ if ((BaseObject *)_windows[i] == object) {
+ _windows.remove_at(i);
+
+ // get new focused window
+ if (_focusedWindow == object) {
+ _focusedWindow = NULL;
+ }
+
+ break;
+ }
+ }
+
+ // is it active object?
+ if (_activeObject == object) {
+ _activeObject = NULL;
+ }
+
+ // is it main object?
+ if (_mainObject == object) {
+ _mainObject = NULL;
+ }
+
+ // destroy object
+ for (uint32 i = 0; i < _regObjects.size(); i++) {
+ if (_regObjects[i] == object) {
+ _regObjects.remove_at(i);
+ if (!_loadInProgress) {
+ SystemClassRegistry::getInstance()->enumInstances(invalidateValues, "ScValue", (void *)object);
+ }
+ delete object;
+ return STATUS_OK;
+ }
+ }
+
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::invalidateValues(void *value, void *data) {
+ ScValue *val = (ScValue *)value;
+ if (val->isNative() && val->getNative() == data) {
+ if (!val->_persistent && ((BaseScriptable *)data)->_refCount == 1) {
+ ((BaseScriptable *)data)->_refCount++;
+ }
+ val->setNative(NULL);
+ val->setNULL();
+ }
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::validObject(BaseObject *object) {
+ if (!object) {
+ return false;
+ }
+ if (object == this) {
+ return true;
+ }
+
+ for (uint32 i = 0; i < _regObjects.size(); i++) {
+ if (_regObjects[i] == object) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name) {
+ ScValue *thisObj;
+
+ //////////////////////////////////////////////////////////////////////////
+ // LOG
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "LOG") == 0) {
+ stack->correctParams(1);
+ _gameRef->LOG(0, "sc: %s", stack->pop()->getString());
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // String
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "String") == 0) {
+ thisObj = thisStack->getTop();
+
+ thisObj->setNative(makeSXString(_gameRef, stack));
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MemBuffer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MemBuffer") == 0) {
+ thisObj = thisStack->getTop();
+
+ thisObj->setNative(makeSXMemBuffer(_gameRef, stack));
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // File
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "File") == 0) {
+ thisObj = thisStack->getTop();
+
+ thisObj->setNative(makeSXFile(_gameRef, stack));
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Date
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Date") == 0) {
+ thisObj = thisStack->getTop();
+
+ thisObj->setNative(makeSXDate(_gameRef, stack));
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Array
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Array") == 0) {
+ thisObj = thisStack->getTop();
+
+ thisObj->setNative(makeSXArray(_gameRef, stack));
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Object
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Object") == 0) {
+ thisObj = thisStack->getTop();
+
+ thisObj->setNative(makeSXObject(_gameRef, stack));
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sleep
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Sleep") == 0) {
+ stack->correctParams(1);
+
+ script->sleep((uint32)stack->pop()->getInt());
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WaitFor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WaitFor") == 0) {
+ stack->correctParams(1);
+
+ BaseScriptable *obj = stack->pop()->getNative();
+ if (validObject((BaseObject *)obj)) {
+ script->waitForExclusive((BaseObject *)obj);
+ }
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Random
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Random") == 0) {
+ stack->correctParams(2);
+
+ int from = stack->pop()->getInt();
+ int to = stack->pop()->getInt();
+
+ stack->pushInt(BaseUtils::randomInt(from, to));
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetScriptTimeSlice
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetScriptTimeSlice") == 0) {
+ stack->correctParams(1);
+
+ script->_timeSlice = (uint32)stack->pop()->getInt();
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MakeRGBA / MakeRGB / RGB
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MakeRGBA") == 0 || strcmp(name, "MakeRGB") == 0 || strcmp(name, "RGB") == 0) {
+ stack->correctParams(4);
+ int r = stack->pop()->getInt();
+ int g = stack->pop()->getInt();
+ int b = stack->pop()->getInt();
+ int a;
+ ScValue *val = stack->pop();
+ if (val->isNULL()) {
+ a = 255;
+ } else {
+ a = val->getInt();
+ }
+
+ stack->pushInt(BYTETORGBA(r, g, b, a));
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MakeHSL
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MakeHSL") == 0) {
+ stack->correctParams(3);
+ int h = stack->pop()->getInt();
+ int s = stack->pop()->getInt();
+ int l = stack->pop()->getInt();
+
+ stack->pushInt(BaseUtils::HSLtoRGB(h, s, l));
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetRValue
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetRValue") == 0) {
+ stack->correctParams(1);
+
+ uint32 rgba = (uint32)stack->pop()->getInt();
+ stack->pushInt(RGBCOLGetR(rgba));
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetGValue
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetGValue") == 0) {
+ stack->correctParams(1);
+
+ uint32 rgba = (uint32)stack->pop()->getInt();
+ stack->pushInt(RGBCOLGetG(rgba));
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetBValue
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetBValue") == 0) {
+ stack->correctParams(1);
+
+ uint32 rgba = (uint32)stack->pop()->getInt();
+ stack->pushInt(RGBCOLGetB(rgba));
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetAValue
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetAValue") == 0) {
+ stack->correctParams(1);
+
+ uint32 rgba = (uint32)stack->pop()->getInt();
+ stack->pushInt(RGBCOLGetA(rgba));
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetHValue
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHValue") == 0) {
+ stack->correctParams(1);
+ uint32 rgb = (uint32)stack->pop()->getInt();
+
+ byte H, S, L;
+ BaseUtils::RGBtoHSL(rgb, &H, &S, &L);
+ stack->pushInt(H);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSValue
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSValue") == 0) {
+ stack->correctParams(1);
+ uint32 rgb = (uint32)stack->pop()->getInt();
+
+ byte H, S, L;
+ BaseUtils::RGBtoHSL(rgb, &H, &S, &L);
+ stack->pushInt(S);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetLValue
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetLValue") == 0) {
+ stack->correctParams(1);
+ uint32 rgb = (uint32)stack->pop()->getInt();
+
+ byte H, S, L;
+ BaseUtils::RGBtoHSL(rgb, &H, &S, &L);
+ stack->pushInt(L);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Debug
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Debug") == 0) {
+ stack->correctParams(0);
+ stack->pushNULL();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ToString") == 0) {
+ stack->correctParams(1);
+ const char *str = stack->pop()->getString();
+ char *str2 = new char[strlen(str) + 1];
+ strcpy(str2, str);
+ stack->pushString(str2);
+ delete[] str2;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToInt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ToInt") == 0) {
+ stack->correctParams(1);
+ int val = stack->pop()->getInt();
+ stack->pushInt(val);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ToFloat") == 0) {
+ stack->correctParams(1);
+ double val = stack->pop()->getFloat();
+ stack->pushFloat(val);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ToBool") == 0) {
+ stack->correctParams(1);
+ bool val = stack->pop()->getBool();
+ stack->pushBool(val);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // failure
+ else {
+ script->runtimeError("Call to undefined function '%s'. Ignored.", name);
+ stack->correctParams(0);
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::showCursor() {
+ if (_cursorHidden) {
+ return STATUS_OK;
+ }
+
+ if (!_interactive && _gameRef->_state == GAME_RUNNING) {
+ if (_cursorNoninteractive) {
+ return drawCursor(_cursorNoninteractive);
+ }
+ } else {
+ if (_activeObject && !DID_FAIL(_activeObject->showCursor())) {
+ return STATUS_OK;
+ } else {
+ if (_activeObject && _activeCursor && _activeObject->getExtendedFlag("usable")) {
+ return drawCursor(_activeCursor);
+ } else if (_cursor) {
+ return drawCursor(_cursor);
+ }
+ }
+ }
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::saveGame(int slot, const char *desc, bool quickSave) {
+ return SaveLoad::saveGame(slot, desc, quickSave, _gameRef);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::loadGame(int slot) {
+ //_gameRef->LOG(0, "Load start %d", BaseUtils::GetUsedMemMB());
+
+ _loading = false;
+ _scheduledLoadSlot = -1;
+
+ Common::String filename = SaveLoad::getSaveSlotFilename(slot);
+
+ return loadGame(filename.c_str());
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::loadGame(const char *filename) {
+ return SaveLoad::loadGame(filename, _gameRef);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::displayWindows(bool inGame) {
+ bool res;
+
+ // did we lose focus? focus topmost window
+ if (_focusedWindow == NULL || !_focusedWindow->_visible || _focusedWindow->_disable) {
+ _focusedWindow = NULL;
+ for (int i = _windows.size() - 1; i >= 0; i--) {
+ if (_windows[i]->_visible && !_windows[i]->_disable) {
+ _focusedWindow = _windows[i];
+ break;
+ }
+ }
+ }
+
+ // display all windows
+ for (uint32 i = 0; i < _windows.size(); i++) {
+ if (_windows[i]->_visible && _windows[i]->_inGame == inGame) {
+
+ res = _windows[i]->display();
+ if (DID_FAIL(res)) {
+ return res;
+ }
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::playMusic(int channel, const char *filename, bool looping, uint32 loopStart) {
+ if (channel >= NUM_MUSIC_CHANNELS) {
+ _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS);
+ return STATUS_FAILED;
+ }
+
+ delete _music[channel];
+ _music[channel] = NULL;
+
+ _music[channel] = new BaseSound(_gameRef);
+ if (_music[channel] && DID_SUCCEED(_music[channel]->setSound(filename, Audio::Mixer::kMusicSoundType, true))) {
+ if (_musicStartTime[channel]) {
+ _music[channel]->setPositionTime(_musicStartTime[channel]);
+ _musicStartTime[channel] = 0;
+ }
+ if (loopStart) {
+ _music[channel]->setLoopStart(loopStart);
+ }
+ return _music[channel]->play(looping);
+ } else {
+ delete _music[channel];
+ _music[channel] = NULL;
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::stopMusic(int channel) {
+ if (channel >= NUM_MUSIC_CHANNELS) {
+ _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS);
+ return STATUS_FAILED;
+ }
+
+ if (_music[channel]) {
+ _music[channel]->stop();
+ delete _music[channel];
+ _music[channel] = NULL;
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::pauseMusic(int channel) {
+ if (channel >= NUM_MUSIC_CHANNELS) {
+ _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS);
+ return STATUS_FAILED;
+ }
+
+ if (_music[channel]) {
+ return _music[channel]->pause();
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::resumeMusic(int channel) {
+ if (channel >= NUM_MUSIC_CHANNELS) {
+ _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS);
+ return STATUS_FAILED;
+ }
+
+ if (_music[channel]) {
+ return _music[channel]->resume();
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::setMusicStartTime(int channel, uint32 time) {
+ if (channel >= NUM_MUSIC_CHANNELS) {
+ _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS);
+ return STATUS_FAILED;
+ }
+
+ _musicStartTime[channel] = time;
+ if (_music[channel] && _music[channel]->isPlaying()) {
+ return _music[channel]->setPositionTime(time);
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::loadSettings(const char *filename) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(SETTINGS)
+ TOKEN_TABLE(GAME)
+ TOKEN_TABLE(STRING_TABLE)
+ TOKEN_TABLE(RESOLUTION)
+ TOKEN_TABLE(REQUIRE_3D_ACCELERATION)
+ TOKEN_TABLE(REQUIRE_SOUND)
+ TOKEN_TABLE(HWTL_MODE)
+ TOKEN_TABLE(ALLOW_WINDOWED_MODE)
+ TOKEN_TABLE(ALLOW_ACCESSIBILITY_TAB)
+ TOKEN_TABLE(ALLOW_ABOUT_TAB)
+ TOKEN_TABLE(ALLOW_ADVANCED)
+ TOKEN_TABLE(ALLOW_DESKTOP_RES)
+ TOKEN_TABLE(REGISTRY_PATH)
+ TOKEN_TABLE(RICH_SAVED_GAMES)
+ TOKEN_TABLE(SAVED_GAME_EXT)
+ TOKEN_TABLE(GUID)
+ TOKEN_TABLE_END
+
+
+ byte *origBuffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (origBuffer == NULL) {
+ _gameRef->LOG(0, "BaseGame::LoadSettings failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret = STATUS_OK;
+
+ byte *buffer = origBuffer;
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_SETTINGS) {
+ _gameRef->LOG(0, "'SETTINGS' keyword expected in game settings file.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_GAME:
+ delete[] _settingsGameFile;
+ _settingsGameFile = new char[strlen((char *)params) + 1];
+ if (_settingsGameFile) {
+ strcpy(_settingsGameFile, (char *)params);
+ }
+ break;
+
+ case TOKEN_STRING_TABLE:
+ if (DID_FAIL(_stringTable->loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_RESOLUTION:
+ parser.scanStr((char *)params, "%d,%d", &_settingsResWidth, &_settingsResHeight);
+ break;
+
+ case TOKEN_REQUIRE_3D_ACCELERATION:
+ parser.scanStr((char *)params, "%b", &_settingsRequireAcceleration);
+ break;
+
+ case TOKEN_REQUIRE_SOUND:
+ parser.scanStr((char *)params, "%b", &_settingsRequireSound);
+ break;
+
+ case TOKEN_HWTL_MODE:
+ parser.scanStr((char *)params, "%d", &_settingsTLMode);
+ break;
+
+ case TOKEN_ALLOW_WINDOWED_MODE:
+ parser.scanStr((char *)params, "%b", &_settingsAllowWindowed);
+ break;
+
+ case TOKEN_ALLOW_DESKTOP_RES:
+ parser.scanStr((char *)params, "%b", &_settingsAllowDesktopRes);
+ break;
+
+ case TOKEN_ALLOW_ADVANCED:
+ parser.scanStr((char *)params, "%b", &_settingsAllowAdvanced);
+ break;
+
+ case TOKEN_ALLOW_ACCESSIBILITY_TAB:
+ parser.scanStr((char *)params, "%b", &_settingsAllowAccessTab);
+ break;
+
+ case TOKEN_ALLOW_ABOUT_TAB:
+ parser.scanStr((char *)params, "%b", &_settingsAllowAboutTab);
+ break;
+
+ case TOKEN_REGISTRY_PATH:
+ //BaseEngine::instance().getRegistry()->setBasePath((char *)params);
+ break;
+
+ case TOKEN_RICH_SAVED_GAMES:
+ parser.scanStr((char *)params, "%b", &_richSavedGames);
+ break;
+
+ case TOKEN_SAVED_GAME_EXT:
+ BaseUtils::setString(&_savedGameExt, (char *)params);
+ break;
+
+ case TOKEN_GUID:
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in game settings '%s'", filename);
+ ret = STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading game settings '%s'", filename);
+ ret = STATUS_FAILED;
+ }
+
+ _settingsAllowWindowed = true; // TODO: These two settings should probably be cleaned out altogether.
+ _compressedSavegames = true;
+
+ delete[] origBuffer;
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::persist(BasePersistenceManager *persistMgr) {
+ if (!persistMgr->getIsSaving()) {
+ cleanup();
+ }
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_activeObject));
+ persistMgr->transfer(TMEMBER(_capturedObject));
+ persistMgr->transfer(TMEMBER(_cursorNoninteractive));
+ persistMgr->transfer(TMEMBER(_editorMode));
+ persistMgr->transfer(TMEMBER(_fader));
+ persistMgr->transfer(TMEMBER(_freezeLevel));
+ persistMgr->transfer(TMEMBER(_focusedWindow));
+ persistMgr->transfer(TMEMBER(_fontStorage));
+ persistMgr->transfer(TMEMBER(_interactive));
+ persistMgr->transfer(TMEMBER(_keyboardState));
+ persistMgr->transfer(TMEMBER(_lastTime));
+ persistMgr->transfer(TMEMBER(_mainObject));
+ for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) {
+ persistMgr->transfer(TMEMBER(_music[i]));
+ persistMgr->transfer(TMEMBER(_musicStartTime[i]));
+ }
+
+ persistMgr->transfer(TMEMBER(_offsetX));
+ persistMgr->transfer(TMEMBER(_offsetY));
+ persistMgr->transfer(TMEMBER(_offsetPercentX));
+ persistMgr->transfer(TMEMBER(_offsetPercentY));
+
+ persistMgr->transfer(TMEMBER(_origInteractive));
+ persistMgr->transfer(TMEMBER_INT(_origState));
+ persistMgr->transfer(TMEMBER(_personalizedSave));
+ persistMgr->transfer(TMEMBER(_quitting));
+
+ _regObjects.persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_scEngine));
+ //persistMgr->transfer(TMEMBER(_soundMgr));
+ persistMgr->transfer(TMEMBER_INT(_state));
+ //persistMgr->transfer(TMEMBER(_surfaceStorage));
+ persistMgr->transfer(TMEMBER(_subtitles));
+ persistMgr->transfer(TMEMBER(_subtitlesSpeed));
+ persistMgr->transfer(TMEMBER(_systemFont));
+ persistMgr->transfer(TMEMBER(_videoFont));
+ persistMgr->transfer(TMEMBER(_videoSubtitles));
+
+ persistMgr->transfer(TMEMBER(_timer));
+ persistMgr->transfer(TMEMBER(_timerDelta));
+ persistMgr->transfer(TMEMBER(_timerLast));
+
+ persistMgr->transfer(TMEMBER(_liveTimer));
+ persistMgr->transfer(TMEMBER(_liveTimerDelta));
+ persistMgr->transfer(TMEMBER(_liveTimerLast));
+
+ persistMgr->transfer(TMEMBER(_musicCrossfadeRunning));
+ persistMgr->transfer(TMEMBER(_musicCrossfadeStartTime));
+ persistMgr->transfer(TMEMBER(_musicCrossfadeLength));
+ persistMgr->transfer(TMEMBER(_musicCrossfadeChannel1));
+ persistMgr->transfer(TMEMBER(_musicCrossfadeChannel2));
+ persistMgr->transfer(TMEMBER(_musicCrossfadeSwap));
+
+ _renderer->persistSaveLoadImages(persistMgr);
+
+ persistMgr->transfer(TMEMBER_INT(_textEncoding));
+ persistMgr->transfer(TMEMBER(_textRTL));
+
+ persistMgr->transfer(TMEMBER(_soundBufferSizeSec));
+ persistMgr->transfer(TMEMBER(_suspendedRendering));
+
+ persistMgr->transfer(TMEMBER(_mouseLockRect));
+
+ _windows.persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_suppressScriptErrors));
+ persistMgr->transfer(TMEMBER(_autorunDisabled));
+
+ persistMgr->transfer(TMEMBER(_autoSaveOnExit));
+ persistMgr->transfer(TMEMBER(_autoSaveSlot));
+ persistMgr->transfer(TMEMBER(_cursorHidden));
+
+ if (!persistMgr->getIsSaving()) {
+ _quitting = false;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::focusWindow(UIWindow *window) {
+ UIWindow *prev = _focusedWindow;
+
+ for (uint32 i = 0; i < _windows.size(); i++) {
+ if (_windows[i] == window) {
+ if (i < _windows.size() - 1) {
+ _windows.remove_at(i);
+ _windows.add(window);
+
+ _gameRef->_focusedWindow = window;
+ }
+
+ if (window->_mode == WINDOW_NORMAL && prev != window && _gameRef->validObject(prev) && (prev->_mode == WINDOW_EXCLUSIVE || prev->_mode == WINDOW_SYSTEM_EXCLUSIVE)) {
+ return focusWindow(prev);
+ } else {
+ return STATUS_OK;
+ }
+ }
+ }
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::freeze(bool includingMusic) {
+ if (_freezeLevel == 0) {
+ _scEngine->pauseAll();
+ _soundMgr->pauseAll(includingMusic);
+ _origState = _state;
+ _origInteractive = _interactive;
+ _interactive = true;
+ }
+ _state = GAME_FROZEN;
+ _freezeLevel++;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::unfreeze() {
+ if (_freezeLevel == 0) {
+ return STATUS_OK;
+ }
+
+ _freezeLevel--;
+ if (_freezeLevel == 0) {
+ _state = _origState;
+ _interactive = _origInteractive;
+ _scEngine->resumeAll();
+ _soundMgr->resumeAll();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::handleKeypress(Common::Event *event, bool printable) {
+ if (isVideoPlaying()) {
+ if (event->kbd.keycode == Common::KEYCODE_ESCAPE) {
+ stopVideo();
+ }
+ return true;
+ }
+
+ if (event->type == Common::EVENT_QUIT) {
+ onWindowClose();
+ return true;
+ }
+
+ _keyboardState->handleKeyPress(event);
+ _keyboardState->readKey(event);
+// TODO
+
+ if (_focusedWindow) {
+ if (!_gameRef->_focusedWindow->handleKeypress(event, _keyboardState->_currentPrintable)) {
+ /*if (event->type != SDL_TEXTINPUT) {*/
+ if (_gameRef->_focusedWindow->canHandleEvent("Keypress")) {
+ _gameRef->_focusedWindow->applyEvent("Keypress");
+ } else {
+ applyEvent("Keypress");
+ }
+ /*}*/
+ }
+ return true;
+ } else { /*if (event->type != SDL_TEXTINPUT)*/
+ applyEvent("Keypress");
+ return true;
+ }
+
+ return false;
+}
+
+void BaseGame::handleKeyRelease(Common::Event *event) {
+ _keyboardState->handleKeyRelease(event);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::handleMouseWheel(int delta) {
+ bool handled = false;
+ if (_focusedWindow) {
+ handled = _gameRef->_focusedWindow->handleMouseWheel(delta);
+
+ if (!handled) {
+ if (delta < 0 && _gameRef->_focusedWindow->canHandleEvent("MouseWheelDown")) {
+ _gameRef->_focusedWindow->applyEvent("MouseWheelDown");
+ handled = true;
+ } else if (_gameRef->_focusedWindow->canHandleEvent("MouseWheelUp")) {
+ _gameRef->_focusedWindow->applyEvent("MouseWheelUp");
+ handled = true;
+ }
+
+ }
+ }
+
+ if (!handled) {
+ if (delta < 0) {
+ applyEvent("MouseWheelDown");
+ } else {
+ applyEvent("MouseWheelUp");
+ }
+ }
+
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor) {
+ if (verMajor) {
+ *verMajor = DCGF_VER_MAJOR;
+ }
+ if (verMinor) {
+ *verMinor = DCGF_VER_MINOR;
+ }
+
+ if (extMajor) {
+ *extMajor = 0;
+ }
+ if (extMinor) {
+ *extMinor = 0;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::setWindowTitle() {
+ if (_renderer) {
+ char title[512];
+ strcpy(title, _caption[0]);
+ if (title[0] != '\0') {
+ strcat(title, " - ");
+ }
+ strcat(title, "WME Lite");
+
+
+ Utf8String utf8Title;
+ if (_textEncoding == TEXT_UTF8) {
+ utf8Title = Utf8String(title);
+ } else {
+ warning("BaseGame::SetWindowTitle - Ignoring textencoding");
+ utf8Title = Utf8String(title);
+ /* WideString wstr = StringUtil::AnsiToWide(Title);
+ title = StringUtil::WideToUtf8(wstr);*/
+ }
+ warning("BaseGame::SetWindowTitle: Ignoring value: %s", utf8Title.c_str());
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::setActiveObject(BaseObject *obj) {
+ // not-active when game is frozen
+ if (obj && !_gameRef->_interactive && !obj->_nonIntMouseEvents) {
+ obj = NULL;
+ }
+
+ if (obj == _activeObject) {
+ return STATUS_OK;
+ }
+
+ if (_activeObject) {
+ _activeObject->applyEvent("MouseLeave");
+ }
+ //if (ValidObject(_activeObject)) _activeObject->applyEvent("MouseLeave");
+ _activeObject = obj;
+ if (_activeObject) {
+ _activeObject->applyEvent("MouseEntry");
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::pushViewport(BaseViewport *viewport) {
+ _viewportSP++;
+ if (_viewportSP >= (int32)_viewportStack.size()) {
+ _viewportStack.add(viewport);
+ } else {
+ _viewportStack[_viewportSP] = viewport;
+ }
+
+ _renderer->setViewport(viewport->getRect());
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::popViewport() {
+ _viewportSP--;
+ if (_viewportSP < -1) {
+ _gameRef->LOG(0, "Fatal: Viewport stack underflow!");
+ }
+
+ if (_viewportSP >= 0 && _viewportSP < (int32)_viewportStack.size()) {
+ _renderer->setViewport(_viewportStack[_viewportSP]->getRect());
+ } else _renderer->setViewport(_renderer->_drawOffsetX,
+ _renderer->_drawOffsetY,
+ _renderer->_width + _renderer->_drawOffsetX,
+ _renderer->_height + _renderer->_drawOffsetY);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::getCurrentViewportRect(Rect32 *rect, bool *custom) {
+ if (rect == NULL) {
+ return STATUS_FAILED;
+ } else {
+ if (_viewportSP >= 0) {
+ BasePlatform::copyRect(rect, _viewportStack[_viewportSP]->getRect());
+ if (custom) {
+ *custom = true;
+ }
+ } else {
+ BasePlatform::setRect(rect, _renderer->_drawOffsetX,
+ _renderer->_drawOffsetY,
+ _renderer->_width + _renderer->_drawOffsetX,
+ _renderer->_height + _renderer->_drawOffsetY);
+ if (custom) {
+ *custom = false;
+ }
+ }
+
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::getCurrentViewportOffset(int *offsetX, int *offsetY) {
+ if (_viewportSP >= 0) {
+ if (offsetX) {
+ *offsetX = _viewportStack[_viewportSP]->_offsetX;
+ }
+ if (offsetY) {
+ *offsetY = _viewportStack[_viewportSP]->_offsetY;
+ }
+ } else {
+ if (offsetX) {
+ *offsetX = 0;
+ }
+ if (offsetY) {
+ *offsetY = 0;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::windowLoadHook(UIWindow *win, char **buf, char **params) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::setInteractive(bool state) {
+ _interactive = state;
+ if (_transMgr) {
+ _transMgr->_origInteractive = state;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::resetMousePos() {
+ Common::Point p;
+ p.x = _mousePos.x + _renderer->_drawOffsetX;
+ p.y = _mousePos.y + _renderer->_drawOffsetY;
+
+ BasePlatform::setCursorPos(p.x, p.y);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::displayContent(bool doUpdate, bool displayAll) {
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::displayContentSimple() {
+ // fill black
+ _renderer->fill(0, 0, 0);
+ _renderer->displayIndicator();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::updateMusicCrossfade() {
+ /* byte globMusicVol = _soundMgr->getVolumePercent(SOUND_MUSIC); */
+
+ if (!_musicCrossfadeRunning) {
+ return STATUS_OK;
+ }
+ if (_state == GAME_FROZEN) {
+ return STATUS_OK;
+ }
+
+ if (_musicCrossfadeChannel1 < 0 || _musicCrossfadeChannel1 >= NUM_MUSIC_CHANNELS || !_music[_musicCrossfadeChannel1]) {
+ _musicCrossfadeRunning = false;
+ return STATUS_OK;
+ }
+ if (_musicCrossfadeChannel2 < 0 || _musicCrossfadeChannel2 >= NUM_MUSIC_CHANNELS || !_music[_musicCrossfadeChannel2]) {
+ _musicCrossfadeRunning = false;
+ return STATUS_OK;
+ }
+
+ if (!_music[_musicCrossfadeChannel1]->isPlaying()) {
+ _music[_musicCrossfadeChannel1]->play();
+ }
+ if (!_music[_musicCrossfadeChannel2]->isPlaying()) {
+ _music[_musicCrossfadeChannel2]->play();
+ }
+
+ uint32 currentTime = _gameRef->_liveTimer - _musicCrossfadeStartTime;
+
+ if (currentTime >= _musicCrossfadeLength) {
+ _musicCrossfadeRunning = false;
+ //_music[_musicCrossfadeChannel2]->setVolume(GlobMusicVol);
+ _music[_musicCrossfadeChannel2]->setVolumePercent(100);
+
+ _music[_musicCrossfadeChannel1]->stop();
+ //_music[_musicCrossfadeChannel1]->setVolume(GlobMusicVol);
+ _music[_musicCrossfadeChannel1]->setVolumePercent(100);
+
+
+ if (_musicCrossfadeSwap) {
+ // swap channels
+ BaseSound *dummy = _music[_musicCrossfadeChannel1];
+ int dummyInt = _musicStartTime[_musicCrossfadeChannel1];
+
+ _music[_musicCrossfadeChannel1] = _music[_musicCrossfadeChannel2];
+ _musicStartTime[_musicCrossfadeChannel1] = _musicStartTime[_musicCrossfadeChannel2];
+
+ _music[_musicCrossfadeChannel2] = dummy;
+ _musicStartTime[_musicCrossfadeChannel2] = dummyInt;
+ }
+ } else {
+ //_music[_musicCrossfadeChannel1]->setVolume(GlobMusicVol - (float)CurrentTime / (float)_musicCrossfadeLength * GlobMusicVol);
+ //_music[_musicCrossfadeChannel2]->setVolume((float)CurrentTime / (float)_musicCrossfadeLength * GlobMusicVol);
+ _music[_musicCrossfadeChannel1]->setVolumePercent((int)(100.0f - (float)currentTime / (float)_musicCrossfadeLength * 100.0f));
+ _music[_musicCrossfadeChannel2]->setVolumePercent((int)((float)currentTime / (float)_musicCrossfadeLength * 100.0f));
+
+ //_gameRef->QuickMessageForm("%d %d", _music[_musicCrossfadeChannel1]->GetVolume(), _music[_musicCrossfadeChannel2]->GetVolume());
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::resetContent() {
+ _scEngine->clearGlobals();
+ //_timer = 0;
+ //_liveTimer = 0;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::DEBUG_DumpClassRegistry() {
+ warning("DEBUG_DumpClassRegistry - untested");
+ Common::DumpFile *f = new Common::DumpFile;
+ f->open("zz_class_reg_dump.log");
+
+ SystemClassRegistry::getInstance()->dumpClasses(f);
+
+ f->close();
+ delete f;
+ _gameRef->quickMessage("Classes dump completed.");
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::invalidateDeviceObjects() {
+ for (uint32 i = 0; i < _regObjects.size(); i++) {
+ _regObjects[i]->invalidateDeviceObjects();
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::restoreDeviceObjects() {
+ for (uint32 i = 0; i < _regObjects.size(); i++) {
+ _regObjects[i]->restoreDeviceObjects();
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::setWaitCursor(const char *filename) {
+ delete _cursorNoninteractive;
+ _cursorNoninteractive = NULL;
+
+ _cursorNoninteractive = new BaseSprite(_gameRef);
+ if (!_cursorNoninteractive || DID_FAIL(_cursorNoninteractive->loadFile(filename))) {
+ delete _cursorNoninteractive;
+ _cursorNoninteractive = NULL;
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::isVideoPlaying() {
+ if (_videoPlayer->isPlaying()) {
+ return true;
+ }
+ if (_theoraPlayer && _theoraPlayer->isPlaying()) {
+ return true;
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::stopVideo() {
+ if (_videoPlayer->isPlaying()) {
+ _videoPlayer->stop();
+ }
+ if (_theoraPlayer && _theoraPlayer->isPlaying()) {
+ _theoraPlayer->stop();
+ delete _theoraPlayer;
+ _theoraPlayer = NULL;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::drawCursor(BaseSprite *cursor) {
+ if (!cursor) {
+ return STATUS_FAILED;
+ }
+ if (cursor != _lastCursor) {
+ cursor->reset();
+ _lastCursor = cursor;
+ }
+ return cursor->draw(_mousePos.x, _mousePos.y);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onActivate(bool activate, bool refreshMouse) {
+ if (_shuttingDown || !_renderer) {
+ return STATUS_OK;
+ }
+
+ _renderer->_active = activate;
+
+ if (refreshMouse) {
+ Point32 p;
+ getMousePos(&p);
+ setActiveObject(_renderer->getObjectAt(p.x, p.y));
+ }
+
+ if (activate) {
+ _soundMgr->resumeAll();
+ } else {
+ _soundMgr->pauseAll();
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onMouseLeftDown() {
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_LEFT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftClick"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("LeftClick");
+ }
+ }
+
+ if (_activeObject != NULL) {
+ _capturedObject = _activeObject;
+ }
+ _mouseLeftDown = true;
+ BasePlatform::setCapture(/*_renderer->_window*/);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onMouseLeftUp() {
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_LEFT);
+ }
+
+ BasePlatform::releaseCapture();
+ _capturedObject = NULL;
+ _mouseLeftDown = false;
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftRelease"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("LeftRelease");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onMouseLeftDblClick() {
+ if (_state == GAME_RUNNING && !_interactive) {
+ return STATUS_OK;
+ }
+
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_LEFT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftDoubleClick"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("LeftDoubleClick");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onMouseRightDblClick() {
+ if (_state == GAME_RUNNING && !_interactive) {
+ return STATUS_OK;
+ }
+
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_RIGHT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightDoubleClick"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("RightDoubleClick");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onMouseRightDown() {
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_RIGHT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightClick"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("RightClick");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onMouseRightUp() {
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_RIGHT);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightRelease"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("RightRelease");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onMouseMiddleDown() {
+ if (_state == GAME_RUNNING && !_interactive) {
+ return STATUS_OK;
+ }
+
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_MIDDLE);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("MiddleClick"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("MiddleClick");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onMouseMiddleUp() {
+ if (_activeObject) {
+ _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_MIDDLE);
+ }
+
+ bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("MiddleRelease"));
+ if (!handled) {
+ if (_activeObject != NULL) {
+ _activeObject->applyEvent("MiddleRelease");
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onPaint() {
+ if (_renderer && _renderer->_windowed && _renderer->_ready) {
+ _renderer->initLoop();
+ displayContent(false, true);
+ displayDebugInfo();
+ _renderer->windowedBlt();
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onWindowClose() {
+ if (canHandleEvent("QuitGame")) {
+ if (_state != GAME_FROZEN) {
+ _gameRef->applyEvent("QuitGame");
+ }
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::displayDebugInfo() {
+ char str[100];
+
+ if (_debugShowFPS) {
+ sprintf(str, "FPS: %d", _gameRef->_fps);
+ _systemFont->drawText((byte *)str, 0, 0, 100, TAL_LEFT);
+ }
+
+ if (_gameRef->_debugDebugMode) {
+ if (!_gameRef->_renderer->_windowed) {
+ sprintf(str, "Mode: %dx%dx%d", _renderer->_width, _renderer->_height, _renderer->_bPP);
+ } else {
+ sprintf(str, "Mode: %dx%d windowed", _renderer->_width, _renderer->_height);
+ }
+
+ strcat(str, " (");
+ strcat(str, _renderer->getName().c_str());
+ strcat(str, ")");
+ _systemFont->drawText((byte *)str, 0, 0, _renderer->_width, TAL_RIGHT);
+
+ _renderer->displayDebugInfo();
+
+ int scrTotal, scrRunning, scrWaiting, scrPersistent;
+ scrTotal = _scEngine->getNumScripts(&scrRunning, &scrWaiting, &scrPersistent);
+ sprintf(str, "Running scripts: %d (r:%d w:%d p:%d)", scrTotal, scrRunning, scrWaiting, scrPersistent);
+ _systemFont->drawText((byte *)str, 0, 70, _renderer->_width, TAL_RIGHT);
+
+
+ sprintf(str, "Timer: %d", _timer);
+ _gameRef->_systemFont->drawText((byte *)str, 0, 130, _renderer->_width, TAL_RIGHT);
+
+ if (_activeObject != NULL) {
+ _systemFont->drawText((const byte *)_activeObject->getName(), 0, 150, _renderer->_width, TAL_RIGHT);
+ }
+
+ sprintf(str, "GfxMem: %dMB", _usedMem / (1024 * 1024));
+ _systemFont->drawText((byte *)str, 0, 170, _renderer->_width, TAL_RIGHT);
+
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::getMousePos(Point32 *pos) {
+ BasePlatform::getCursorPos(pos);
+
+ pos->x -= _renderer->_drawOffsetX;
+ pos->y -= _renderer->_drawOffsetY;
+
+ /*
+ // Windows can squish maximized window if it's larger than desktop
+ // so we need to modify mouse position appropriately (tnx mRax)
+ if (_renderer->_windowed && ::IsZoomed(_renderer->_window)) {
+ Common::Rect rc;
+ ::GetClientRect(_renderer->_window, &rc);
+ Pos->x *= _gameRef->_renderer->_realWidth;
+ Pos->x /= (rc.right - rc.left);
+ Pos->y *= _gameRef->_renderer->_realHeight;
+ Pos->y /= (rc.bottom - rc.top);
+ }
+ */
+
+ if (_mouseLockRect.left != 0 && _mouseLockRect.right != 0 && _mouseLockRect.top != 0 && _mouseLockRect.bottom != 0) {
+ if (!BasePlatform::ptInRect(&_mouseLockRect, *pos)) {
+ pos->x = MAX(_mouseLockRect.left, pos->x);
+ pos->y = MAX(_mouseLockRect.top, pos->y);
+
+ pos->x = MIN(_mouseLockRect.right, pos->x);
+ pos->y = MIN(_mouseLockRect.bottom, pos->y);
+
+ Point32 newPos = *pos;
+
+ newPos.x += _renderer->_drawOffsetX;
+ newPos.y += _renderer->_drawOffsetY;
+
+ BasePlatform::setCursorPos(newPos.x, newPos.y);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::miniUpdate() { // TODO: Is this really necessary, it used to update sound, but the mixer does that now.
+ if (!_miniUpdateEnabled) {
+ return;
+ }
+
+ if (g_system->getMillis() - _lastMiniUpdate > 200) {
+ _lastMiniUpdate = g_system->getMillis();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::onScriptShutdown(ScScript *script) {
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::isLeftDoubleClick() {
+ return isDoubleClick(0);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::isRightDoubleClick() {
+ return isDoubleClick(1);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseGame::isDoubleClick(int buttonIndex) {
+ uint32 maxDoubleCLickTime = 500;
+ int maxMoveX = 4;
+ int maxMoveY = 4;
+
+ Point32 pos;
+ BasePlatform::getCursorPos(&pos);
+
+ int moveX = abs(pos.x - _lastClick[buttonIndex].posX);
+ int moveY = abs(pos.y - _lastClick[buttonIndex].posY);
+
+
+ if (_lastClick[buttonIndex].time == 0 || g_system->getMillis() - _lastClick[buttonIndex].time > maxDoubleCLickTime || moveX > maxMoveX || moveY > maxMoveY) {
+ _lastClick[buttonIndex].time = g_system->getMillis();
+ _lastClick[buttonIndex].posX = pos.x;
+ _lastClick[buttonIndex].posY = pos.y;
+ return false;
+ } else {
+ _lastClick[buttonIndex].time = 0;
+ return true;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::autoSaveOnExit() {
+ _soundMgr->saveSettings();
+ ConfMan.flushToDisk();
+
+ if (!_autoSaveOnExit) {
+ return;
+ }
+ if (_state == GAME_FROZEN) {
+ return;
+ }
+
+ saveGame(_autoSaveSlot, "autosave", true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseGame::addMem(int bytes) {
+ _usedMem += bytes;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString BaseGame::getDeviceType() const {
+ return "computer";
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_game.h b/engines/wintermute/base/base_game.h
new file mode 100644
index 0000000000..0f764b3d03
--- /dev/null
+++ b/engines/wintermute/base/base_game.h
@@ -0,0 +1,361 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_GAME_H
+#define WINTERMUTE_BASE_GAME_H
+
+#include "engines/wintermute/base/base_object.h"
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/math/rect32.h"
+#include "common/events.h"
+
+namespace Wintermute {
+
+typedef void (*ENGINE_LOG_CALLBACK)(char *text, bool result, void *data);
+
+class BaseSoundMgr;
+class BaseFader;
+class BaseFont;
+class BaseFileManager;
+class BaseTransitionMgr;
+class ScEngine;
+class BaseFontStorage;
+class BaseStringTable;
+class BaseQuickMsg;
+class UIWindow;
+class BaseViewport;
+class BaseRenderer;
+class BaseRegistry;
+class BaseSaveThumbHelper;
+class BaseSurfaceStorage;
+class SXMath;
+class BaseKeyboardState;
+class VideoPlayer;
+class VideoTheoraPlayer;
+
+#define NUM_MUSIC_CHANNELS 5
+
+class BaseGame: public BaseObject {
+public:
+ DECLARE_PERSISTENT(BaseGame, BaseObject)
+
+ virtual bool onScriptShutdown(ScScript *script);
+
+ virtual bool onActivate(bool activate, bool refreshMouse);
+ virtual bool onMouseLeftDown();
+ virtual bool onMouseLeftUp();
+ virtual bool onMouseLeftDblClick();
+ virtual bool onMouseRightDblClick();
+ virtual bool onMouseRightDown();
+ virtual bool onMouseRightUp();
+ virtual bool onMouseMiddleDown();
+ virtual bool onMouseMiddleUp();
+ virtual bool onPaint();
+ virtual bool onWindowClose();
+
+ bool isLeftDoubleClick();
+ bool isRightDoubleClick();
+
+ bool _autorunDisabled;
+
+ uint32 _lastMiniUpdate;
+ bool _miniUpdateEnabled;
+
+ virtual void miniUpdate();
+
+ void getMousePos(Point32 *Pos);
+ Rect32 _mouseLockRect;
+
+ bool _shuttingDown;
+
+ virtual bool displayDebugInfo();
+ bool _debugShowFPS;
+
+ bool _suspendedRendering;
+ int _soundBufferSizeSec;
+
+ TTextEncoding _textEncoding;
+ bool _textRTL;
+
+ virtual bool resetContent();
+
+ void DEBUG_DumpClassRegistry();
+ bool setWaitCursor(const char *filename);
+
+ int _thumbnailWidth;
+ int _thumbnailHeight;
+
+ bool _editorMode;
+ void getOffset(int *offsetX, int *offsetY);
+ void setOffset(int offsetX, int offsetY);
+ int getSequence();
+
+ int _offsetY;
+ int _offsetX;
+ float _offsetPercentX;
+ float _offsetPercentY;
+ BaseObject *_mainObject;
+
+ bool initInput();
+ bool initLoop();
+ uint32 _currentTime;
+ uint32 _deltaTime;
+ BaseFont *_systemFont;
+ BaseFont *_videoFont;
+ bool initialize1();
+ bool initialize2();
+ bool initialize3();
+ BaseTransitionMgr *_transMgr;
+
+ void LOG(bool res, const char *fmt, ...);
+
+ BaseRenderer *_renderer;
+ BaseSoundMgr *_soundMgr;
+ ScEngine *_scEngine;
+ SXMath *_mathClass;
+ BaseSurfaceStorage *_surfaceStorage;
+ BaseFontStorage *_fontStorage;
+ BaseGame(const Common::String &gameId);
+ virtual ~BaseGame();
+
+ void DEBUG_DebugDisable();
+ void DEBUG_DebugEnable(const char *filename = NULL);
+ bool _debugDebugMode;
+
+ void *_debugLogFile;
+ int _sequence;
+ virtual bool loadFile(const char *filename);
+ virtual bool loadBuffer(byte *buffer, bool complete = true);
+
+ int _viewportSP;
+
+ BaseStringTable *_stringTable;
+ int _settingsResWidth;
+ int _settingsResHeight;
+ char *_settingsGameFile;
+ bool _suppressScriptErrors;
+ bool _mouseLeftDown; // TODO: Hide
+
+ virtual bool externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name);
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+ // compatibility bits
+ bool _compatKillMethodThreads;
+
+ const char* getGameId() { return _gameId.c_str(); }
+ void setGameId(const Common::String& gameId) { _gameId = gameId; }
+ uint32 _surfaceGCCycleTime;
+ bool _smartCache; // RO
+ bool _subtitles; // RO
+
+ int _scheduledLoadSlot;
+ bool _loading;
+
+ virtual bool handleMouseWheel(int delta);
+ bool _quitting;
+ virtual bool getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor);
+
+ virtual bool handleKeypress(Common::Event *event, bool printable = false);
+ virtual void handleKeyRelease(Common::Event *event);
+
+ bool unfreeze();
+ bool freeze(bool includingMusic = true);
+ bool focusWindow(UIWindow *window);
+ bool _loadInProgress;
+ UIWindow *_focusedWindow;
+ bool _editorForceScripts;
+
+ static void invalidateValues(void *value, void *data);
+
+ bool loadSettings(const char *filename);
+
+ bool displayWindows(bool inGame = false);
+ bool _useD3D;
+ virtual bool cleanup();
+ bool loadGame(int slot);
+ bool loadGame(const char *filename);
+ bool saveGame(int slot, const char *desc, bool quickSave = false);
+ virtual bool showCursor();
+
+ BaseObject *_activeObject;
+
+ bool _interactive;
+ TGameState _state;
+ TGameState _origState;
+ bool _origInteractive;
+ uint32 _timer;
+ uint32 _timerDelta;
+ uint32 _timerLast;
+
+ uint32 _liveTimer;
+ uint32 _liveTimerDelta;
+ uint32 _liveTimerLast;
+
+ BaseObject *_capturedObject;
+ Point32 _mousePos;
+ bool validObject(BaseObject *object);
+ bool unregisterObject(BaseObject *object);
+ bool registerObject(BaseObject *object);
+ void quickMessage(const char *text);
+ void quickMessageForm(char *fmt, ...);
+ bool displayQuickMsg();
+
+ virtual bool displayContent(bool update = true, bool displayAll = false);
+ virtual bool displayContentSimple();
+ bool _forceNonStreamedSounds;
+ void resetMousePos();
+ int _subtitlesSpeed;
+ void setInteractive(bool state);
+ virtual bool windowLoadHook(UIWindow *win, char **buf, char **params);
+ virtual bool windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name);
+ bool getCurrentViewportOffset(int *offsetX = NULL, int *offsetY = NULL);
+ bool getCurrentViewportRect(Rect32 *rect, bool *custom = NULL);
+ bool popViewport();
+ bool pushViewport(BaseViewport *Viewport);
+ bool setActiveObject(BaseObject *Obj);
+ BaseSprite *_lastCursor;
+ bool drawCursor(BaseSprite *Cursor);
+
+ BaseSaveThumbHelper *_cachedThumbnail;
+ void addMem(int bytes);
+ bool _touchInterface;
+ bool _constrainedMemory;
+protected:
+ BaseSprite *_loadingIcon;
+ int _loadingIconX;
+ int _loadingIconY;
+ int _loadingIconPersistent;
+
+ BaseFader *_fader;
+
+ int _freezeLevel;
+ VideoPlayer *_videoPlayer;
+ VideoTheoraPlayer *_theoraPlayer;
+private:
+ bool _mouseRightDown;
+ bool _mouseMidlleDown;
+ bool _settingsRequireAcceleration;
+ bool _settingsAllowWindowed;
+ bool _settingsAllowAdvanced;
+ bool _settingsAllowAccessTab;
+ bool _settingsAllowAboutTab;
+ bool _settingsRequireSound;
+ bool _settingsAllowDesktopRes;
+ int _settingsTLMode;
+ virtual bool invalidateDeviceObjects();
+ virtual bool restoreDeviceObjects();
+
+ char *_localSaveDir;
+ bool _saveDirChecked;
+ bool _richSavedGames;
+ char *_savedGameExt;
+
+ bool _reportTextureFormat;
+
+ // FPS stuff
+ uint32 _lastTime;
+ uint32 _fpsTime;
+ uint32 _framesRendered;
+ Common::String _gameId;
+
+ void setEngineLogCallback(ENGINE_LOG_CALLBACK callback = NULL, void *data = NULL);
+ ENGINE_LOG_CALLBACK _engineLogCallback;
+ void *_engineLogCallbackData;
+
+ bool _videoSubtitles;
+ uint32 _musicStartTime[NUM_MUSIC_CHANNELS];
+ bool _compressedSavegames;
+
+ bool _personalizedSave;
+
+ void setWindowTitle();
+
+ bool resumeMusic(int channel);
+ bool setMusicStartTime(int channel, uint32 time);
+ bool pauseMusic(int channel);
+ bool stopMusic(int channel);
+ bool playMusic(int channel, const char *filename, bool looping = true, uint32 loopStart = 0);
+ BaseSound *_music[NUM_MUSIC_CHANNELS];
+ bool _musicCrossfadeRunning;
+ bool _musicCrossfadeSwap;
+ uint32 _musicCrossfadeStartTime;
+ uint32 _musicCrossfadeLength;
+ int _musicCrossfadeChannel1;
+ int _musicCrossfadeChannel2;
+
+ BaseSprite *_cursorNoninteractive;
+ BaseKeyboardState *_keyboardState;
+
+ uint32 _fps;
+ bool updateMusicCrossfade();
+
+ bool isVideoPlaying();
+ bool stopVideo();
+
+ BaseArray<BaseQuickMsg *> _quickMessages;
+ BaseArray<UIWindow *> _windows;
+ BaseArray<BaseViewport *> _viewportStack;
+ BaseArray<BaseObject *> _regObjects;
+
+ AnsiString getDeviceType() const;
+
+ struct LastClickInfo {
+ LastClickInfo() {
+ posX = posY = 0;
+ time = 0;
+ }
+
+ int posX;
+ int posY;
+ uint32 time;
+ };
+
+ LastClickInfo _lastClick[2];
+ bool isDoubleClick(int buttonIndex);
+ uint32 _usedMem;
+
+
+
+protected:
+ // WME Lite specific
+ bool _autoSaveOnExit;
+ int _autoSaveSlot;
+ bool _cursorHidden;
+
+public:
+ void autoSaveOnExit();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_keyboard_state.cpp b/engines/wintermute/base/base_keyboard_state.cpp
new file mode 100644
index 0000000000..da7baafd2d
--- /dev/null
+++ b/engines/wintermute/base/base_keyboard_state.cpp
@@ -0,0 +1,309 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_keyboard_state.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "common/system.h"
+#include "common/keyboard.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseKeyboardState, false)
+
+//////////////////////////////////////////////////////////////////////////
+BaseKeyboardState::BaseKeyboardState(BaseGame *inGame) : BaseScriptable(inGame) {
+ _currentPrintable = false;
+ _currentCharCode = 0;
+ _currentKeyData = 0;
+
+ _currentShift = false;
+ _currentAlt = false;
+ _currentControl = false;
+
+ _keyStates = new uint8[323]; // Hardcoded size for the common/keyboard.h enum
+ for (int i = 0; i < 323; i++) {
+ _keyStates[i] = false;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseKeyboardState::~BaseKeyboardState() {
+ delete[] _keyStates;
+}
+
+void BaseKeyboardState::handleKeyPress(Common::Event *event) {
+ if (event->type == Common::EVENT_KEYDOWN) {
+ _keyStates[event->kbd.keycode] = true;
+ }
+}
+
+void BaseKeyboardState::handleKeyRelease(Common::Event *event) {
+ if (event->type == Common::EVENT_KEYUP) {
+ _keyStates[event->kbd.keycode] = false;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseKeyboardState::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // IsKeyDown
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "IsKeyDown") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ int vKey;
+
+ if (val->_type == VAL_STRING && strlen(val->getString()) > 0) {
+ const char *str = val->getString();
+ char temp = str[0];
+ if (temp >= 'A' && temp <= 'Z') {
+ temp += ('a' - 'A');
+ }
+ vKey = (int)temp;
+ } else {
+ vKey = val->getInt();
+ }
+
+ bool isDown = _keyStates[vKeyToKeyCode(vKey)];
+
+ stack->pushBool(isDown);
+ return STATUS_OK;
+ } else {
+ return BaseScriptable::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseKeyboardState::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("keyboard");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Key
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Key") {
+ if (_currentPrintable) {
+ char key[2];
+ key[0] = (char)_currentCharCode;
+ key[1] = '\0';
+ _scValue->setString(key);
+ } else {
+ _scValue->setString("");
+ }
+
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Printable
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Printable") {
+ _scValue->setBool(_currentPrintable);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // KeyCode
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "KeyCode") {
+ _scValue->setInt(_currentCharCode);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsShift
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "IsShift") {
+ _scValue->setBool(_currentShift);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsAlt
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "IsAlt") {
+ _scValue->setBool(_currentAlt);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsControl
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "IsControl") {
+ _scValue->setBool(_currentControl);
+ return _scValue;
+ } else {
+ return BaseScriptable::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseKeyboardState::scSetProperty(const char *name, ScValue *value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ if (_renderer) SetWindowText(_renderer->_window, _name);
+ return STATUS_OK;
+ }
+
+ else*/ return BaseScriptable::scSetProperty(name, value);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseKeyboardState::scToString() {
+ return "[keyboard state]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseKeyboardState::readKey(Common::Event *event) {
+ //_currentPrintable = (event->type == SDL_TEXTINPUT); // TODO
+ _currentCharCode = keyCodeToVKey(event);
+ if ((_currentCharCode <= Common::KEYCODE_z && _currentCharCode >= Common::KEYCODE_a) ||
+ (_currentCharCode <= Common::KEYCODE_9 && _currentCharCode >= Common::KEYCODE_0) ||
+ (_currentCharCode == Common::KEYCODE_SPACE)) {
+ _currentPrintable = true;
+ } else {
+ _currentPrintable = false;
+ }
+ //_currentKeyData = KeyData;
+
+ _currentControl = isControlDown();
+ _currentAlt = isAltDown();
+ _currentShift = isShiftDown();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseKeyboardState::persist(BasePersistenceManager *persistMgr) {
+ //if (!persistMgr->getIsSaving()) cleanup();
+ BaseScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_currentAlt));
+ persistMgr->transfer(TMEMBER(_currentCharCode));
+ persistMgr->transfer(TMEMBER(_currentControl));
+ persistMgr->transfer(TMEMBER(_currentKeyData));
+ persistMgr->transfer(TMEMBER(_currentPrintable));
+ persistMgr->transfer(TMEMBER(_currentShift));
+
+ if (!persistMgr->getIsSaving()) {
+ _keyStates = new uint8[323]; // Hardcoded size for the common/keyboard.h enum
+ for (int i = 0; i < 323; i++) {
+ _keyStates[i] = false;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseKeyboardState::isShiftDown() {
+ int mod = g_system->getEventManager()->getModifierState();
+ return (mod & Common::KBD_SHIFT);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseKeyboardState::isControlDown() {
+ int mod = g_system->getEventManager()->getModifierState();
+ return (mod & Common::KBD_CTRL);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseKeyboardState::isAltDown() {
+ int mod = g_system->getEventManager()->getModifierState();
+ return (mod & Common::KBD_ALT);
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseKeyboardState::keyCodeToVKey(Common::Event *event) {
+ if (event->type != Common::EVENT_KEYDOWN) {
+ return 0;
+ }
+
+ switch (event->kbd.keycode) {
+ case Common::KEYCODE_KP_ENTER:
+ return Common::KEYCODE_RETURN;
+ default:
+ return (uint32)event->kbd.keycode;
+ }
+}
+
+enum VKeyCodes {
+ kVkSpace = 32,
+ kVkLeft = 37,
+ kVkUp = 38,
+ kVkRight = 39,
+ kVkDown = 40
+};
+
+//////////////////////////////////////////////////////////////////////////
+Common::KeyCode BaseKeyboardState::vKeyToKeyCode(uint32 vkey) {
+ // todo
+ switch (vkey) {
+ case kVkSpace:
+ return Common::KEYCODE_SPACE;
+ break;
+ case kVkLeft:
+ return Common::KEYCODE_LEFT;
+ break;
+ case kVkRight:
+ return Common::KEYCODE_RIGHT;
+ break;
+ case kVkUp:
+ return Common::KEYCODE_UP;
+ break;
+ case kVkDown:
+ return Common::KEYCODE_DOWN;
+ break;
+ default:
+ warning("Unknown VKEY: %d", vkey);
+ return (Common::KeyCode)vkey;
+ break;
+ }
+
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_keyboard_state.h b/engines/wintermute/base/base_keyboard_state.h
new file mode 100644
index 0000000000..dfd0efdec0
--- /dev/null
+++ b/engines/wintermute/base/base_keyboard_state.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_KEYBOARD_STATE_H
+#define WINTERMUTE_BASE_KEYBOARD_STATE_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/base/base_scriptable.h"
+#include "common/keyboard.h"
+#include "common/events.h"
+
+namespace Wintermute {
+
+class BaseKeyboardState : public BaseScriptable {
+public:
+ uint32 _currentKeyData;
+ uint32 _currentCharCode;
+ bool _currentPrintable;
+
+ bool _currentShift;
+ bool _currentAlt;
+ bool _currentControl;
+
+ DECLARE_PERSISTENT(BaseKeyboardState, BaseScriptable)
+ BaseKeyboardState(BaseGame *inGame);
+ virtual ~BaseKeyboardState();
+ bool readKey(Common::Event *event);
+
+ void handleKeyPress(Common::Event *event);
+ void handleKeyRelease(Common::Event *event);
+ static bool isShiftDown();
+ static bool isControlDown();
+ static bool isAltDown();
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+
+private:
+ uint8 *_keyStates;
+ uint32 keyCodeToVKey(Common::Event *event);
+ Common::KeyCode vKeyToKeyCode(uint32 vkey); //TODO, reimplement using ScummVM-backend
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_named_object.cpp b/engines/wintermute/base/base_named_object.cpp
new file mode 100644
index 0000000000..915bf24d7f
--- /dev/null
+++ b/engines/wintermute/base/base_named_object.cpp
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_named_object.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+BaseNamedObject::BaseNamedObject(BaseGame *inGame) : BaseClass(inGame) {
+ _name = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseNamedObject::BaseNamedObject() : BaseClass() {
+ _name = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseNamedObject::BaseNamedObject(TDynamicConstructor, TDynamicConstructor) {
+ _name = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseNamedObject::~BaseNamedObject(void) {
+ delete[] _name;
+ _name = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseNamedObject::setName(const char *name) {
+ delete[] _name;
+ _name = NULL;
+
+ if (name == NULL) {
+ return;
+ }
+
+ _name = new char [strlen(name) + 1];
+ if (_name != NULL) {
+ strcpy(_name, name);
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_named_object.h b/engines/wintermute/base/base_named_object.h
new file mode 100644
index 0000000000..77a00cee45
--- /dev/null
+++ b/engines/wintermute/base/base_named_object.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_NAMED_OBJECT_H
+#define WINTERMUTE_BASE_NAMED_OBJECT_H
+
+
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+class BaseNamedObject : public BaseClass {
+ char *_name;
+public:
+ BaseNamedObject(BaseGame *inGame);
+ BaseNamedObject();
+ virtual ~BaseNamedObject(void);
+ BaseNamedObject(TDynamicConstructor, TDynamicConstructor);
+
+ const char *getName() { return _name; }
+ void setName(const char *name);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_object.cpp b/engines/wintermute/base/base_object.cpp
new file mode 100644
index 0000000000..eba8416485
--- /dev/null
+++ b/engines/wintermute/base/base_object.cpp
@@ -0,0 +1,1246 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_object.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/platform_osystem.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseObject, false)
+
+//////////////////////////////////////////////////////////////////////
+BaseObject::BaseObject(BaseGame *inGame) : BaseScriptHolder(inGame) {
+ _posX = _posY = 0;
+ _movable = true;
+ _zoomable = true;
+ _registrable = true;
+ _shadowable = true;
+ _rotatable = false;
+ _is3D = false;
+
+ _alphaColor = 0;
+ _scale = -1;
+ _relativeScale = 0;
+
+ _scaleX = -1;
+ _scaleY = -1;
+
+ _ready = true;
+
+ _soundEvent = NULL;
+
+ _iD = _gameRef->getSequence();
+
+ BasePlatform::setRectEmpty(&_rect);
+ _rectSet = false;
+
+ _cursor = NULL;
+ _activeCursor = NULL;
+ _sharedCursors = false;
+
+ _sFX = NULL;
+ _sFXStart = 0;
+ _sFXVolume = 100;
+ _autoSoundPanning = true;
+
+ _editorAlwaysRegister = false;
+ _editorSelected = false;
+
+ _editorOnly = false;
+
+ _rotate = 0.0f;
+ _rotateValid = false;
+ _relativeRotate = 0.0f;
+
+ for (int i = 0; i < 7; i++) {
+ _caption[i] = NULL;
+ }
+ _saveState = true;
+
+ _nonIntMouseEvents = false;
+
+ // sound FX
+ _sFXType = SFX_NONE;
+ _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0;
+
+ _blendMode = BLEND_NORMAL;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseObject::~BaseObject() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::cleanup() {
+ if (_gameRef && _gameRef->_activeObject == this) {
+ _gameRef->_activeObject = NULL;
+ }
+
+ BaseScriptHolder::cleanup();
+ delete[] _soundEvent;
+ _soundEvent = NULL;
+
+ if (!_sharedCursors) {
+ delete _cursor;
+ delete _activeCursor;
+ _cursor = NULL;
+ _activeCursor = NULL;
+ }
+ delete _sFX;
+ _sFX = NULL;
+
+ for (int i = 0; i < 7; i++) {
+ delete[] _caption[i];
+ _caption[i] = NULL;
+ }
+
+ _sFXType = SFX_NONE;
+ _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseObject::setCaption(const char *caption, int caseVal) {
+ if (caseVal == 0) {
+ caseVal = 1;
+ }
+ if (caseVal < 1 || caseVal > 7) {
+ return;
+ }
+
+ delete[] _caption[caseVal - 1];
+ _caption[caseVal - 1] = new char[strlen(caption) + 1];
+ if (_caption[caseVal - 1]) {
+ strcpy(_caption[caseVal - 1], caption);
+ _gameRef->_stringTable->expand(&_caption[caseVal - 1]);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseObject::getCaption(int caseVal) {
+ if (caseVal == 0) {
+ caseVal = 1;
+ }
+ if (caseVal < 1 || caseVal > 7 || _caption[caseVal - 1] == NULL) {
+ return "";
+ } else {
+ return _caption[caseVal - 1];
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::listen(BaseScriptHolder *param1, uint32 param2) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // SkipTo
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SkipTo") == 0) {
+ stack->correctParams(2);
+ _posX = stack->pop()->getInt();
+ _posY = stack->pop()->getInt();
+ afterMove();
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Caption
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Caption") == 0) {
+ stack->correctParams(1);
+ stack->pushString(getCaption(stack->pop()->getInt()));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetCursor") == 0) {
+ stack->correctParams(1);
+ if (DID_SUCCEED(setCursor(stack->pop()->getString()))) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RemoveCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemoveCursor") == 0) {
+ stack->correctParams(0);
+ if (!_sharedCursors) {
+ delete _cursor;
+ _cursor = NULL;
+ } else {
+ _cursor = NULL;
+
+ }
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetCursor") == 0) {
+ stack->correctParams(0);
+ if (!_cursor || !_cursor->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_cursor->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetCursorObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetCursorObject") == 0) {
+ stack->correctParams(0);
+ if (!_cursor) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_cursor, true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HasCursor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HasCursor") == 0) {
+ stack->correctParams(0);
+
+ if (_cursor) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetCaption
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetCaption") == 0) {
+ stack->correctParams(2);
+ setCaption(stack->pop()->getString(), stack->pop()->getInt());
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadSound
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadSound") == 0) {
+ stack->correctParams(1);
+ const char *filename = stack->pop()->getString();
+ if (DID_SUCCEED(playSFX(filename, false, false))) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PlaySound
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PlaySound") == 0) {
+ stack->correctParams(3);
+
+ const char *filename;
+ bool looping;
+ uint32 loopStart;
+
+ ScValue *val1 = stack->pop();
+ ScValue *val2 = stack->pop();
+ ScValue *val3 = stack->pop();
+
+ if (val1->_type == VAL_BOOL) {
+ filename = NULL;
+ looping = val1->getBool();
+ loopStart = val2->getInt();
+ } else {
+ if (val1->isNULL()) {
+ filename = NULL;
+ } else {
+ filename = val1->getString();
+ }
+ looping = val2->isNULL() ? false : val2->getBool();
+ loopStart = val3->getInt();
+ }
+
+ if (DID_FAIL(playSFX(filename, looping, true, NULL, loopStart))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PlaySoundEvent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PlaySoundEvent") == 0) {
+ stack->correctParams(2);
+
+ const char *filename;
+ const char *eventName;
+
+ ScValue *val1 = stack->pop();
+ ScValue *val2 = stack->pop();
+
+ if (val2->isNULL()) {
+ filename = NULL;
+ eventName = val1->getString();
+ } else {
+ filename = val1->getString();
+ eventName = val2->getString();
+ }
+
+ if (DID_FAIL(playSFX(filename, false, true, eventName))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // StopSound
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "StopSound") == 0) {
+ stack->correctParams(0);
+
+ if (DID_FAIL(stopSFX())) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PauseSound
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PauseSound") == 0) {
+ stack->correctParams(0);
+
+ if (DID_FAIL(pauseSFX())) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ResumeSound
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ResumeSound") == 0) {
+ stack->correctParams(0);
+
+ if (DID_FAIL(resumeSFX())) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsSoundPlaying
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsSoundPlaying") == 0) {
+ stack->correctParams(0);
+
+ if (_sFX && _sFX->isPlaying()) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetSoundPosition
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetSoundPosition") == 0) {
+ stack->correctParams(1);
+
+ uint32 time = stack->pop()->getInt();
+ if (DID_FAIL(setSFXTime(time))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSoundPosition
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSoundPosition") == 0) {
+ stack->correctParams(0);
+
+ if (!_sFX) {
+ stack->pushInt(0);
+ } else {
+ stack->pushInt(_sFX->getPositionTime());
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetSoundVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetSoundVolume") == 0) {
+ stack->correctParams(1);
+
+ int volume = stack->pop()->getInt();
+ if (DID_FAIL(setSFXVolume(volume))) {
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetSoundVolume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSoundVolume") == 0) {
+ stack->correctParams(0);
+
+ if (!_sFX) {
+ stack->pushInt(_sFXVolume);
+ } else {
+ stack->pushInt(_sFX->getVolumePercent());
+ }
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SoundFXNone
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SoundFXNone") == 0) {
+ stack->correctParams(0);
+ _sFXType = SFX_NONE;
+ _sFXParam1 = 0;
+ _sFXParam2 = 0;
+ _sFXParam3 = 0;
+ _sFXParam4 = 0;
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SoundFXEcho
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SoundFXEcho") == 0) {
+ stack->correctParams(4);
+ _sFXType = SFX_ECHO;
+ _sFXParam1 = (float)stack->pop()->getFloat(0); // Wet/Dry Mix [%] (0-100)
+ _sFXParam2 = (float)stack->pop()->getFloat(0); // Feedback [%] (0-100)
+ _sFXParam3 = (float)stack->pop()->getFloat(333.0f); // Left Delay [ms] (1-2000)
+ _sFXParam4 = (float)stack->pop()->getFloat(333.0f); // Right Delay [ms] (1-2000)
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SoundFXReverb
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SoundFXReverb") == 0) {
+ stack->correctParams(4);
+ _sFXType = SFX_REVERB;
+ _sFXParam1 = (float)stack->pop()->getFloat(0); // In Gain [dB] (-96 - 0)
+ _sFXParam2 = (float)stack->pop()->getFloat(0); // Reverb Mix [dB] (-96 - 0)
+ _sFXParam3 = (float)stack->pop()->getFloat(1000.0f); // Reverb Time [ms] (0.001 - 3000)
+ _sFXParam4 = (float)stack->pop()->getFloat(0.001f); // HighFreq RT Ratio (0.001 - 0.999)
+ stack->pushNULL();
+
+ return STATUS_OK;
+ } else {
+ return BaseScriptHolder::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseObject::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("object");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Caption
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Caption") {
+ _scValue->setString(getCaption(1));
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // X
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "X") {
+ _scValue->setInt(_posX);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Y
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Y") {
+ _scValue->setInt(_posY);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Height (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Height") {
+ _scValue->setInt(getHeight());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Ready (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Ready") {
+ _scValue->setBool(_ready);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Movable
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Movable") {
+ _scValue->setBool(_movable);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Registrable/Interactive
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Registrable" || name == "Interactive") {
+ _scValue->setBool(_registrable);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Zoomable/Scalable
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Zoomable" || name == "Scalable") {
+ _scValue->setBool(_zoomable);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Rotatable
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Rotatable") {
+ _scValue->setBool(_rotatable);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AlphaColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AlphaColor") {
+ _scValue->setInt((int)_alphaColor);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // BlendMode
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "BlendMode") {
+ _scValue->setInt((int)_blendMode);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Scale
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Scale") {
+ if (_scale < 0) {
+ _scValue->setNULL();
+ } else {
+ _scValue->setFloat((double)_scale);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScaleX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScaleX") {
+ if (_scaleX < 0) {
+ _scValue->setNULL();
+ } else {
+ _scValue->setFloat((double)_scaleX);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScaleY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScaleY") {
+ if (_scaleY < 0) {
+ _scValue->setNULL();
+ } else {
+ _scValue->setFloat((double)_scaleY);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RelativeScale
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "RelativeScale") {
+ _scValue->setFloat((double)_relativeScale);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Rotate
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Rotate") {
+ if (!_rotateValid) {
+ _scValue->setNULL();
+ } else {
+ _scValue->setFloat((double)_rotate);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RelativeRotate
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "RelativeRotate") {
+ _scValue->setFloat((double)_relativeRotate);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Colorable
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Colorable") {
+ _scValue->setBool(_shadowable);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SoundPanning
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SoundPanning") {
+ _scValue->setBool(_autoSoundPanning);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SaveState
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SaveState") {
+ _scValue->setBool(_saveState);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NonIntMouseEvents
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NonIntMouseEvents") {
+ _scValue->setBool(_nonIntMouseEvents);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccCaption
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccCaption") {
+ _scValue->setNULL();
+ return _scValue;
+ } else {
+ return BaseScriptHolder::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Caption
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Caption") == 0) {
+ setCaption(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // X
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "X") == 0) {
+ _posX = value->getInt();
+ afterMove();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Y
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Y") == 0) {
+ _posY = value->getInt();
+ afterMove();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Movable
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Movable") == 0) {
+ _movable = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Registrable/Interactive
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Registrable") == 0 || strcmp(name, "Interactive") == 0) {
+ _registrable = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Zoomable/Scalable
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Zoomable") == 0 || strcmp(name, "Scalable") == 0) {
+ _zoomable = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Rotatable
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Rotatable") == 0) {
+ _rotatable = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AlphaColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AlphaColor") == 0) {
+ _alphaColor = (uint32)value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // BlendMode
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "BlendMode") == 0) {
+ int i = value->getInt();
+ if (i < BLEND_NORMAL || i >= NUM_BLEND_MODES) {
+ i = BLEND_NORMAL;
+ }
+ _blendMode = (TSpriteBlendMode)i;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Scale
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Scale") == 0) {
+ if (value->isNULL()) {
+ _scale = -1;
+ } else {
+ _scale = (float)value->getFloat();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScaleX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScaleX") == 0) {
+ if (value->isNULL()) {
+ _scaleX = -1;
+ } else {
+ _scaleX = (float)value->getFloat();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ScaleY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScaleY") == 0) {
+ if (value->isNULL()) {
+ _scaleY = -1;
+ } else {
+ _scaleY = (float)value->getFloat();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RelativeScale
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RelativeScale") == 0) {
+ _relativeScale = (float)value->getFloat();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Rotate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Rotate") == 0) {
+ if (value->isNULL()) {
+ _rotate = 0.0f;
+ _rotateValid = false;
+ } else {
+ _rotate = (float)value->getFloat();
+ _rotateValid = true;
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RelativeRotate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RelativeRotate") == 0) {
+ _relativeRotate = (float)value->getFloat();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Colorable
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Colorable") == 0) {
+ _shadowable = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SoundPanning
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SoundPanning") == 0) {
+ _autoSoundPanning = value->getBool();
+ if (!_autoSoundPanning) {
+ resetSoundPan();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SaveState
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SaveState") == 0) {
+ _saveState = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NonIntMouseEvents
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "NonIntMouseEvents") == 0) {
+ _nonIntMouseEvents = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccCaption
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AccCaption") == 0) {
+ return STATUS_OK;
+ } else {
+ return BaseScriptHolder::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseObject::scToString() {
+ return "[object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::showCursor() {
+ if (_cursor) {
+ return _gameRef->drawCursor(_cursor);
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::persist(BasePersistenceManager *persistMgr) {
+ BaseScriptHolder::persist(persistMgr);
+
+ for (int i = 0; i < 7; i++) {
+ persistMgr->transfer(TMEMBER(_caption[i]));
+ }
+ persistMgr->transfer(TMEMBER(_activeCursor));
+ persistMgr->transfer(TMEMBER(_alphaColor));
+ persistMgr->transfer(TMEMBER(_autoSoundPanning));
+ persistMgr->transfer(TMEMBER(_cursor));
+ persistMgr->transfer(TMEMBER(_sharedCursors));
+ persistMgr->transfer(TMEMBER(_editorAlwaysRegister));
+ persistMgr->transfer(TMEMBER(_editorOnly));
+ persistMgr->transfer(TMEMBER(_editorSelected));
+ persistMgr->transfer(TMEMBER(_iD));
+ persistMgr->transfer(TMEMBER(_is3D));
+ persistMgr->transfer(TMEMBER(_movable));
+ persistMgr->transfer(TMEMBER(_posX));
+ persistMgr->transfer(TMEMBER(_posY));
+ persistMgr->transfer(TMEMBER(_relativeScale));
+ persistMgr->transfer(TMEMBER(_rotatable));
+ persistMgr->transfer(TMEMBER(_scale));
+ persistMgr->transfer(TMEMBER(_sFX));
+ persistMgr->transfer(TMEMBER(_sFXStart));
+ persistMgr->transfer(TMEMBER(_sFXVolume));
+ persistMgr->transfer(TMEMBER(_ready));
+ persistMgr->transfer(TMEMBER(_rect));
+ persistMgr->transfer(TMEMBER(_rectSet));
+ persistMgr->transfer(TMEMBER(_registrable));
+ persistMgr->transfer(TMEMBER(_shadowable));
+ persistMgr->transfer(TMEMBER(_soundEvent));
+ persistMgr->transfer(TMEMBER(_zoomable));
+
+ persistMgr->transfer(TMEMBER(_scaleX));
+ persistMgr->transfer(TMEMBER(_scaleY));
+
+ persistMgr->transfer(TMEMBER(_rotate));
+ persistMgr->transfer(TMEMBER(_rotateValid));
+ persistMgr->transfer(TMEMBER(_relativeRotate));
+
+ persistMgr->transfer(TMEMBER(_saveState));
+ persistMgr->transfer(TMEMBER(_nonIntMouseEvents));
+
+ persistMgr->transfer(TMEMBER_INT(_sFXType));
+ persistMgr->transfer(TMEMBER(_sFXParam1));
+ persistMgr->transfer(TMEMBER(_sFXParam2));
+ persistMgr->transfer(TMEMBER(_sFXParam3));
+ persistMgr->transfer(TMEMBER(_sFXParam4));
+
+
+ persistMgr->transfer(TMEMBER_INT(_blendMode));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::setCursor(const char *filename) {
+ if (!_sharedCursors) {
+ delete _cursor;
+ _cursor = NULL;
+ }
+
+ _sharedCursors = false;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile(filename))) {
+ delete _cursor;
+ _cursor = NULL;
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::setActiveCursor(const char *filename) {
+ delete _activeCursor;
+ _activeCursor = new BaseSprite(_gameRef);
+ if (!_activeCursor || DID_FAIL(_activeCursor->loadFile(filename))) {
+ delete _activeCursor;
+ _activeCursor = NULL;
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseObject::getHeight() {
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::handleMouse(TMouseEvent event, TMouseButton button) {
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::handleKeypress(Common::Event *event, bool printable) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::handleMouseWheel(int delta) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::playSFX(const char *filename, bool looping, bool playNow, const char *eventName, uint32 loopStart) {
+ // just play loaded sound
+ if (filename == NULL && _sFX) {
+ if (_gameRef->_editorMode || _sFXStart) {
+ _sFX->setVolumePercent(_sFXVolume);
+ _sFX->setPositionTime(_sFXStart);
+ if (!_gameRef->_editorMode) {
+ _sFXStart = 0;
+ }
+ }
+ if (playNow) {
+ setSoundEvent(eventName);
+ if (loopStart) {
+ _sFX->setLoopStart(loopStart);
+ }
+ return _sFX->play(looping);
+ } else {
+ return STATUS_OK;
+ }
+ }
+
+ if (filename == NULL) {
+ return STATUS_FAILED;
+ }
+
+ // create new sound
+ delete _sFX;
+
+ _sFX = new BaseSound(_gameRef);
+ if (_sFX && DID_SUCCEED(_sFX->setSound(filename, Audio::Mixer::kSFXSoundType, true))) {
+ _sFX->setVolumePercent(_sFXVolume);
+ if (_sFXStart) {
+ _sFX->setPositionTime(_sFXStart);
+ _sFXStart = 0;
+ }
+ _sFX->applyFX(_sFXType, _sFXParam1, _sFXParam2, _sFXParam3, _sFXParam4);
+ if (playNow) {
+ setSoundEvent(eventName);
+ if (loopStart) {
+ _sFX->setLoopStart(loopStart);
+ }
+ return _sFX->play(looping);
+ } else {
+ return STATUS_OK;
+ }
+ } else {
+ delete _sFX;
+ _sFX = NULL;
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::stopSFX(bool deleteSound) {
+ if (_sFX) {
+ _sFX->stop();
+ if (deleteSound) {
+ delete _sFX;
+ _sFX = NULL;
+ }
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::pauseSFX() {
+ if (_sFX) {
+ return _sFX->pause();
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::resumeSFX() {
+ if (_sFX) {
+ return _sFX->resume();
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::setSFXTime(uint32 time) {
+ _sFXStart = time;
+ if (_sFX && _sFX->isPlaying()) {
+ return _sFX->setPositionTime(time);
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::setSFXVolume(int volume) {
+ _sFXVolume = volume;
+ if (_sFX) {
+ return _sFX->setVolumePercent(volume);
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::updateSounds() {
+ if (_soundEvent) {
+ if (_sFX && !_sFX->isPlaying()) {
+ applyEvent(_soundEvent);
+ setSoundEvent(NULL);
+ }
+ }
+
+ if (_sFX) {
+ updateOneSound(_sFX);
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::updateOneSound(BaseSound *sound) {
+ bool ret = STATUS_OK;
+
+ if (sound) {
+ if (_autoSoundPanning) {
+ ret = sound->setPan(_gameRef->_soundMgr->posToPan(_posX - _gameRef->_offsetX, _posY - _gameRef->_offsetY));
+ }
+
+ ret = sound->applyFX(_sFXType, _sFXParam1, _sFXParam2, _sFXParam3, _sFXParam4);
+ }
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::resetSoundPan() {
+ if (!_sFX) {
+ return STATUS_OK;
+ } else {
+ return _sFX->setPan(0.0f);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::getExtendedFlag(const char *flagName) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::isReady() {
+ return _ready;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseObject::setSoundEvent(const char *eventName) {
+ delete[] _soundEvent;
+ _soundEvent = NULL;
+ if (eventName) {
+ _soundEvent = new char[strlen(eventName) + 1];
+ if (_soundEvent) {
+ strcpy(_soundEvent, eventName);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseObject::afterMove() {
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_object.h b/engines/wintermute/base/base_object.h
new file mode 100644
index 0000000000..d7d91a25f6
--- /dev/null
+++ b/engines/wintermute/base/base_object.h
@@ -0,0 +1,147 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_OBJECT_H
+#define WINTERMUTE_BASE_OBJECT_H
+
+
+#include "engines/wintermute/base/base_script_holder.h"
+#include "engines/wintermute/persistent.h"
+#include "common/events.h"
+
+namespace Wintermute {
+
+class BaseSprite;
+class BaseSound;
+class BaseSurface;
+class BaseScriptHolder;
+class ScValue;
+class ScStack;
+class ScScript;
+class BaseObject : public BaseScriptHolder {
+protected:
+ bool _autoSoundPanning;
+ uint32 _sFXStart;
+ bool setSFXTime(uint32 time);
+ bool setSFXVolume(int volume);
+ bool resumeSFX();
+ bool pauseSFX();
+ bool stopSFX(bool deleteSound = true);
+ bool playSFX(const char *filename, bool looping = false, bool playNow = true, const char *eventName = NULL, uint32 loopStart = 0);
+ BaseSound *_sFX;
+ TSFXType _sFXType;
+ float _sFXParam1;
+ float _sFXParam2;
+ float _sFXParam3;
+ float _sFXParam4;
+ float _relativeRotate;
+ bool _rotateValid;
+ float _rotate;
+ void setSoundEvent(const char *eventName);
+ bool _rotatable;
+ float _scaleX;
+ float _scaleY;
+ float _relativeScale;
+ bool _editorSelected;
+ bool _editorAlwaysRegister;
+ bool _ready;
+ Rect32 _rect;
+ bool _rectSet;
+ int _iD;
+ char *_soundEvent;
+public:
+ TSpriteBlendMode _blendMode;
+ virtual bool afterMove();
+ float _scale;
+ uint32 _alphaColor;
+ virtual bool isReady();
+ virtual bool getExtendedFlag(const char *flagName);
+ virtual bool resetSoundPan();
+ virtual bool updateSounds();
+ bool updateOneSound(BaseSound *sound);
+ int _sFXVolume;
+
+ virtual bool handleMouseWheel(int delta);
+ virtual bool handleMouse(TMouseEvent event, TMouseButton button);
+ virtual bool handleKeypress(Common::Event *event, bool printable = false);
+ virtual int getHeight();
+ bool setCursor(const char *filename);
+ bool setActiveCursor(const char *filename);
+ bool cleanup();
+ const char *getCaption(int caseVal = 1);
+ void setCaption(const char *caption, int caseVal = 1);
+
+ bool _editorOnly;
+ bool _is3D;
+
+ DECLARE_PERSISTENT(BaseObject, BaseScriptHolder)
+ virtual bool showCursor();
+ BaseSprite *_cursor;
+ bool _sharedCursors;
+ BaseSprite *_activeCursor;
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ virtual bool listen(BaseScriptHolder *param1, uint32 param2);
+
+ bool _movable;
+ bool _zoomable;
+ bool _shadowable;
+ int _posY;
+ int _posX;
+ bool _registrable;
+ char *_caption[7];
+ bool _saveState;
+
+ BaseObject(BaseGame *inGame);
+ virtual ~BaseObject();
+ // base
+ virtual bool update() {
+ return STATUS_FAILED;
+ };
+ virtual bool display() {
+ return STATUS_FAILED;
+ };
+ virtual bool invalidateDeviceObjects() {
+ return STATUS_OK;
+ };
+ virtual bool restoreDeviceObjects() {
+ return STATUS_OK;
+ };
+ bool _nonIntMouseEvents;
+
+
+public:
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_parser.cpp b/engines/wintermute/base/base_parser.cpp
new file mode 100644
index 0000000000..9a0e9e3ad9
--- /dev/null
+++ b/engines/wintermute/base/base_parser.cpp
@@ -0,0 +1,467 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+#include "common/util.h"
+
+#define WHITESPACE " \t\n\r"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////////////////////
+BaseParser::BaseParser() {
+ _whiteSpace = new char [strlen(WHITESPACE) + 1];
+ strcpy(_whiteSpace, WHITESPACE);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseParser::~BaseParser() {
+ if (_whiteSpace != NULL) {
+ delete[] _whiteSpace;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+char *BaseParser::getLastOffender() {
+ return _lastOffender;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int32 BaseParser::getObject(char **buf, const TokenDesc *tokens, char **name, char **data) {
+ skipCharacters(buf, _whiteSpace);
+
+ // skip comment lines.
+ while (**buf == ';') {
+ *buf = strchr(*buf, '\n');
+ _parserLine++;
+ skipCharacters(buf, _whiteSpace);
+ }
+
+ if (! **buf) { // at end of file
+ return PARSERR_EOF;
+ }
+
+ // find the token.
+ // TODO: for now just use brute force. Improve later.
+ while (tokens->id != 0) {
+ if (!scumm_strnicmp(tokens->token, *buf, strlen(tokens->token))) {
+ // here we could be matching PART of a string
+ // we could detect this here or the token list
+ // could just have the longer tokens first in the list
+ break;
+ }
+ ++tokens;
+ }
+ if (tokens->id == 0) {
+ char *p = strchr(*buf, '\n');
+ if (p && p > *buf) {
+ strncpy(_lastOffender, *buf, MIN((uint32)255, (uint32)(p - *buf))); // TODO, clean
+ } else {
+ strcpy(_lastOffender, "");
+ }
+
+ return PARSERR_TOKENNOTFOUND;
+ }
+ // skip the token
+ *buf += strlen(tokens->token);
+ skipCharacters(buf, _whiteSpace);
+
+ // get optional name
+ *name = getSubText(buf, '\'', '\''); // single quotes
+ skipCharacters(buf, _whiteSpace);
+
+ // get optional data
+ if (**buf == '=') { // An assignment rather than a command/object.
+ *data = getAssignmentText(buf);
+ } else {
+ *data = getSubText(buf, '{', '}');
+ }
+
+ return tokens->id;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int32 BaseParser::getCommand(char **buf, const TokenDesc *tokens, char **params) {
+ if (!*buf) {
+ return PARSERR_TOKENNOTFOUND;
+ }
+ BaseEngine::instance().getGameRef()->miniUpdate();
+ char *name;
+ return getObject(buf, tokens, &name, params);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseParser::skipCharacters(char **buf, const char *toSkip) {
+ char ch;
+ while ((ch = **buf) != 0) {
+ if (ch == '\n') {
+ _parserLine++;
+ }
+ if (strchr(toSkip, ch) == NULL) {
+ return;
+ }
+ ++*buf; // skip this character
+ }
+ // we must be at the end of the buffer if we get here
+}
+
+
+//////////////////////////////////////////////////////////////////////
+char *BaseParser::getSubText(char **buf, char open, char close) {
+ if (**buf == 0 || **buf != open) {
+ return 0;
+ }
+ ++*buf; // skip opening delimiter
+ char *result = *buf;
+
+ // now find the closing delimiter
+ char theChar;
+ long skip = 1;
+
+ if (open == close) { // we cant nest identical delimiters
+ open = 0;
+ }
+ while ((theChar = **buf) != 0) {
+ if (theChar == open) {
+ ++skip;
+ }
+ if (theChar == close) {
+ if (--skip == 0) {
+ **buf = 0; // null terminate the result string
+ ++*buf; // move past the closing delimiter
+ break;
+ }
+ }
+ ++*buf; // try next character
+ }
+ return result;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+char *BaseParser::getAssignmentText(char **buf) {
+ ++*buf; // skip the '='
+ skipCharacters(buf, _whiteSpace);
+ char *result = *buf;
+
+
+ if (*result == '"') {
+ result = getSubText(buf, '"', '"');
+ } else {
+ // now, we need to find the next whitespace to end the data
+ char theChar;
+
+ while ((theChar = **buf) != 0) {
+ if (theChar <= 0x20) { // space and control chars
+ break;
+ }
+ ++*buf;
+ }
+ **buf = 0; // null terminate it
+ if (theChar) { // skip the terminator
+ ++*buf;
+ }
+ }
+
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////
+Common::String BaseParser::getToken(char **buf) {
+ char token[100]; // TODO: Remove static
+ char *b = *buf, *t = token;
+ while (true) {
+ while (*b && (*b == ' ' || *b == '\n' || *b == 13 || *b == 10 || *b == '\t')) {
+ b++;
+ }
+ if (*b == ';')
+ while (*b && *b != '\n' && *b != 13 && *b != 10) {
+ b++;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (*b == '\'') {
+ b++;
+ while (*b && *b != '\'') {
+ *t++ = *b++;
+ }
+ *t++ = 0;
+ if (*b == '\'') {
+ b++;
+ }
+ } else if (*b == '(' || *b == ')' || *b == '=' || *b == ',' || *b == '[' || *b == ']' ||
+ *b == '%' || *b == ':' || *b == '{' || *b == '}') {
+ *t++ = *b++;
+ *t++ = 0;
+ } else if (*b == '.' && (*(b + 1) < '0' || *(b + 1) > '9')) {
+ *t++ = *b++;
+ *t++ = 0;
+ } else if ((*b >= '0' && *b <= '9') || *b == '.' || *b == '-') {
+ while (*b && ((*b >= '0' && *b <= '9') || *b == '.' || *b == '-')) {
+ *t++ = *b++;
+ }
+ *t++ = 0;
+ } else if ((*b >= 'A' && *b <= 'Z') || (*b >= 'a' && *b <= 'z') || *b == '_') {
+ while (*b && ((*b >= 'A' && *b <= 'Z') || (*b >= 'a' && *b <= 'z') || *b == '_')) {
+ *t++ = *b++;
+ }
+ *t++ = 0;
+ } else if (*b == 0) {
+ *buf = b;
+ return NULL;
+ } else {
+ // Error.
+ return NULL;
+ }
+
+ *buf = b;
+ return token;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+float BaseParser::getTokenFloat(char **buf) {
+ Common::String token = getToken(buf);
+ const char *t = token.c_str();
+ if (!((*t >= '0' && *t <= '9') || *t == '-' || *t == '.')) {
+ // Error situation. We handle this by return 0.
+ return 0.;
+ }
+ float rc = (float)atof(t);
+ return rc;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int BaseParser::getTokenInt(char **buf) {
+ Common::String token = getToken(buf);
+ const char *t = token.c_str();
+ if (!((*t >= '0' && *t <= '9') || *t == '-')) {
+ // Error situation. We handle this by return 0.
+ return 0;
+ }
+ int rc = atoi(t);
+ return rc;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseParser::skipToken(char **buf, char *tok, char * /*msg*/) {
+ Common::String token = getToken(buf);
+ const char *t = token.c_str();
+ if (strcmp(t, tok)) {
+ return; // Error
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int BaseParser::scanStr(const char *in, const char *format, ...) {
+ va_list arg;
+ va_start(arg, format);
+
+ int num = 0;
+ in += strspn(in, " \t\n\f");
+
+ while (*format && *in) {
+ if (*format == '%') {
+ format++;
+ switch (*format) {
+ case 'd': {
+ int *a = va_arg(arg, int *);
+ in += strspn(in, " \t\n\f");
+ *a = atoi(in);
+ in += strspn(in, "0123456789+- \t\n\f");
+ num++;
+ break;
+ }
+ case 'D': {
+ int i;
+ int *list = va_arg(arg, int *);
+ int *nr = va_arg(arg, int *);
+ in += strspn(in, " \t\n\f");
+ i = 0;
+ while ((*in >= '0' && *in <= '9') || *in == '+' || *in == '-') {
+ list[i++] = atoi(in);
+ in += strspn(in, "0123456789+-");
+ in += strspn(in, " \t\n\f");
+ if (*in != ',') {
+ break;
+ }
+ in++;
+ in += strspn(in, " \t\n\f");
+ }
+ *nr = i;
+ num++;
+ break;
+ }
+ case 'b': {
+ bool *a = va_arg(arg, bool *);
+ in += strspn(in, " \t\n\f");
+ const char *in2 = in + strspn(in, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ int l = (int)(in2 - in);
+
+ *a = (bool)(!scumm_strnicmp(in, "yes", l) || !scumm_strnicmp(in, "true", l) || !scumm_strnicmp(in, "on", l) ||
+ !scumm_strnicmp(in, "1", l));
+
+
+ in = in2 + strspn(in2, " \t\n\f");
+ num++;
+ break;
+ }
+ case 'f': {
+ float *a = va_arg(arg, float *);
+ in += strspn(in, " \t\n\f");
+ *a = (float)atof(in);
+ in += strspn(in, "0123456789.eE+- \t\n\f");
+ num++;
+ break;
+ }
+ case 'F': {
+ int i;
+ float *list = va_arg(arg, float *);
+ int *nr = va_arg(arg, int *);
+ in += strspn(in, " \t\n\f");
+ i = 0;
+ while ((*in >= '0' && *in <= '9') || *in == '.' || *in == '+' || *in == '-' || *in == 'e' || *in == 'E') {
+ list[i++] = (float)atof(in);
+ in += strspn(in, "0123456789.eE+-");
+ in += strspn(in, " \t\n\f");
+ if (*in != ',') {
+ break;
+ }
+ in++;
+ in += strspn(in, " \t\n\f");
+ }
+ *nr = i;
+ num++;
+ break;
+ }
+ case 's': {
+ char *a = va_arg(arg, char *);
+ in += strspn(in, " \t\n\f");
+ if (*in == '\'') {
+ in++;
+ const char *in2 = strchr(in, '\'');
+ if (in2) {
+ Common::strlcpy(a, in, (int)(in2 - in) + 1);
+ in = in2 + 1;
+ } else {
+ strcpy(a, in);
+ in = strchr(in, 0);
+ }
+ } else {
+ const char *in2 = in + strspn(in, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789.");
+ Common::strlcpy(a, in, (int)(in2 - in) + 1);
+ in = in2;
+ }
+ in += strspn(in, " \t\n\f");
+ num++;
+ break;
+ }
+ case 'S': {
+ char *a = va_arg(arg, char *);
+ in += strspn(in, " \t\n\f");
+ if (*in == '\"') {
+ in++;
+ while (*in != '\"') {
+ if (*in == '\\') {
+ in++;
+ switch (*in) {
+ case '\\':
+ *a++ = '\\';
+ break;
+ case 'n':
+ *a++ = '\n';
+ break;
+ case 'r':
+ *a++ = '\r';
+ break;
+ case 't':
+ *a++ = '\t';
+ break;
+ case '"':
+ *a++ = '"';
+ break;
+ default:
+ *a++ = '\\';
+ *a++ = *in;
+ break;
+ } //switch
+ in++;
+ } else {
+ *a++ = *in++;
+ }
+ } //while in string
+ in++;
+ num++;
+ } //if string started
+
+ //terminate string
+ *a = '\0';
+ break;
+ }
+ }
+ if (*format) {
+ format++;
+ }
+ } else if (*format == ' ') {
+ format++;
+ in += strspn(in, " \t\n\f");
+ } else if (*in == *format) {
+ in++;
+ format++;
+ } else {
+ num = -1;
+ break;
+ }
+ }
+
+ va_end(arg);
+
+ return num;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_parser.h b/engines/wintermute/base/base_parser.h
new file mode 100644
index 0000000000..76ca8ea856
--- /dev/null
+++ b/engines/wintermute/base/base_parser.h
@@ -0,0 +1,88 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_PARSER_H
+#define WINTERMUTE_BASE_PARSER_H
+
+
+#define TOKEN_DEF_START \
+ enum \
+ { \
+ TOKEN_NONE = 0,
+#define TOKEN_DEF(name) \
+ TOKEN_ ## name,
+#define TOKEN_DEF_END \
+ TOKEN_TOTAL_COUNT \
+ };
+#define TOKEN_TABLE_START(name) \
+ static const BaseParser::TokenDesc name [] = \
+ {
+#define TOKEN_TABLE(name) \
+ { TOKEN_ ## name, #name },
+#define TOKEN_TABLE_END \
+ { 0, 0 } \
+ };
+
+#define PARSERR_GENERIC -3
+#define PARSERR_EOF -2
+#define PARSERR_TOKENNOTFOUND -1
+
+#include "engines/wintermute/coll_templ.h"
+
+namespace Wintermute {
+
+class BaseParser {
+public:
+ struct TokenDesc {
+ int32 id;
+ const char *token;
+ };
+
+public:
+ int scanStr(const char *in, const char *format, ...);
+ int32 getCommand(char **buf, const TokenDesc *tokens, char **params);
+ BaseParser();
+ virtual ~BaseParser();
+private:
+ char *getLastOffender();
+ void skipToken(char **buf, char *tok, char *msg = NULL);
+ int getTokenInt(char **buf);
+ float getTokenFloat(char **buf);
+ Common::String getToken(char **buf);
+ char *getAssignmentText(char **buf);
+ char *getSubText(char **buf, char open, char close);
+ void skipCharacters(char **buf, const char *toSkip);
+ int32 getObject(char **buf, const TokenDesc *tokens, char **name, char **data);
+ int _parserLine;
+ char _lastOffender[255];
+ char *_whiteSpace;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp
new file mode 100644
index 0000000000..4cb67b87e1
--- /dev/null
+++ b/engines/wintermute/base/base_persistence_manager.cpp
@@ -0,0 +1,828 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/dcgf.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+#include "engines/wintermute/base/base_save_thumb_helper.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/math/vector2.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/wintermute.h"
+#include "graphics/decoders/bmp.h"
+#include "common/memstream.h"
+#include "common/str.h"
+#include "common/system.h"
+#include "common/savefile.h"
+
+namespace Wintermute {
+
+#define SAVE_BUFFER_INIT_SIZE 100000
+#define SAVE_BUFFER_GROW_BY 50000
+
+#define SAVE_MAGIC 0x45564153
+#define SAVE_MAGIC_2 0x32564153
+
+//////////////////////////////////////////////////////////////////////////
+BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool deleteSingleton) {
+ _saving = false;
+// _buffer = NULL;
+// _bufferSize = 0;
+ _offset = 0;
+ _saveStream = NULL;
+ _loadStream = NULL;
+ _deleteSingleton = deleteSingleton;
+ if (BaseEngine::instance().getGameRef()) {
+ _gameRef = BaseEngine::instance().getGameRef();
+ } else {
+ _gameRef = NULL;
+ }
+
+ _richBuffer = NULL;
+ _richBufferSize = 0;
+
+ _savedDescription = NULL;
+// _savedTimestamp = 0;
+ _savedVerMajor = _savedVerMinor = _savedVerBuild = 0;
+ _savedExtMajor = _savedExtMinor = 0;
+
+ _thumbnailDataSize = 0;
+ _thumbnailData = NULL;
+ if (savePrefix) {
+ _savePrefix = savePrefix;
+ } else if (_gameRef) {
+ _savePrefix = _gameRef->getGameId();
+ } else {
+ _savePrefix = "wmesav";
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BasePersistenceManager::~BasePersistenceManager() {
+ cleanup();
+ if (_deleteSingleton && BaseEngine::instance().getGameRef() == NULL)
+ BaseEngine::destroy();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BasePersistenceManager::cleanup() {
+ /* if (_buffer) {
+ if (_saving) free(_buffer);
+ else delete[] _buffer; // allocated by file manager
+ }
+ _buffer = NULL;
+
+ _bufferSize = 0;*/
+ _offset = 0;
+
+ delete[] _richBuffer;
+ _richBuffer = NULL;
+ _richBufferSize = 0;
+
+ delete[] _savedDescription;
+ _savedDescription = NULL; // ref to buffer
+// _savedTimestamp = 0;
+ _savedVerMajor = _savedVerMinor = _savedVerBuild = 0;
+ _savedExtMajor = _savedExtMinor = 0;
+
+ _thumbnailDataSize = 0;
+ if (_thumbnailData) {
+ delete[] _thumbnailData;
+ _thumbnailData = NULL;
+ }
+
+ delete _loadStream;
+ delete _saveStream;
+ _loadStream = NULL;
+ _saveStream = NULL;
+}
+
+Common::String BasePersistenceManager::getFilenameForSlot(int slot) const {
+ // 3 Digits, to allow for one save-slot for autosave + slot 1 - 100 (which will be numbered 0-99 filename-wise)
+ return Common::String::format("%s-save%03d.wsv", _savePrefix.c_str(), slot);
+}
+
+void BasePersistenceManager::getSaveStateDesc(int slot, SaveStateDescriptor &desc) {
+ Common::String filename = getFilenameForSlot(slot);
+ debugC(kWintermuteDebugSaveGame, "Trying to list savegame %s in slot %d", filename.c_str(), slot);
+ if (DID_FAIL(readHeader(filename))) {
+ warning("getSavedDesc(%d) - Failed for %s", slot, filename.c_str());
+ return;
+ }
+ desc.setSaveSlot(slot);
+ desc.setDescription(_savedDescription);
+ desc.setDeletableFlag(true);
+ desc.setWriteProtectedFlag(false);
+
+ if (_thumbnailDataSize > 0) {
+ Common::MemoryReadStream thumbStream(_thumbnailData, _thumbnailDataSize);
+ Graphics::BitmapDecoder bmpDecoder;
+ if (bmpDecoder.loadStream(thumbStream)) {
+ Graphics::Surface *surf = new Graphics::Surface;
+ surf = bmpDecoder.getSurface()->convertTo(g_system->getOverlayFormat());
+ desc.setThumbnail(surf);
+ }
+ }
+
+ desc.setSaveDate(_savedTimestamp.tm_year, _savedTimestamp.tm_mon, _savedTimestamp.tm_mday);
+ desc.setSaveTime(_savedTimestamp.tm_hour, _savedTimestamp.tm_min);
+ desc.setPlayTime(0);
+}
+
+void BasePersistenceManager::deleteSaveSlot(int slot) {
+ Common::String filename = getFilenameForSlot(slot);
+ g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+uint32 BasePersistenceManager::getMaxUsedSlot() {
+ Common::String saveMask = Common::String::format("%s-save???.wsv", _savePrefix.c_str());
+ Common::StringArray saves = g_system->getSavefileManager()->listSavefiles(saveMask);
+ Common::StringArray::iterator it = saves.begin();
+ int ret = -1;
+ for (; it != saves.end(); ++it) {
+ int num = -1;
+ sscanf(it->c_str(), "save%d", &num);
+ ret = MAX(ret, num);
+ }
+ return ret;
+}
+
+bool BasePersistenceManager::getSaveExists(int slot) {
+ Common::String filename = getFilenameForSlot(slot);
+ if (DID_FAIL(readHeader(filename))) {
+ return false;
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePersistenceManager::initSave(const char *desc) {
+ if (!desc) {
+ return STATUS_FAILED;
+ }
+
+ cleanup();
+ _saving = true;
+
+ _saveStream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
+
+ if (_saveStream) {
+ // get thumbnails
+ if (!_gameRef->_cachedThumbnail) {
+ _gameRef->_cachedThumbnail = new BaseSaveThumbHelper(_gameRef);
+ if (DID_FAIL(_gameRef->_cachedThumbnail->storeThumbnail(true))) {
+ delete _gameRef->_cachedThumbnail;
+ _gameRef->_cachedThumbnail = NULL;
+ }
+ }
+
+ uint32 magic = DCGF_MAGIC;
+ putDWORD(magic);
+
+ magic = SAVE_MAGIC_2;
+ putDWORD(magic);
+
+ byte verMajor, verMinor, extMajor, extMinor;
+ _gameRef->getVersion(&verMajor, &verMinor, &extMajor, &extMinor);
+ //uint32 version = MAKELONG(MAKEWORD(VerMajor, VerMinor), MAKEWORD(ExtMajor, ExtMinor));
+ _saveStream->writeByte(verMajor);
+ _saveStream->writeByte(verMinor);
+ _saveStream->writeByte(extMajor);
+ _saveStream->writeByte(extMinor);
+
+ // new in ver 2
+ putDWORD((uint32)DCGF_VER_BUILD);
+ putString(_gameRef->getName());
+
+ // thumbnail data size
+ bool thumbnailOK = false;
+
+ if (_gameRef->_cachedThumbnail) {
+ if (_gameRef->_cachedThumbnail->_thumbnail) {
+ Common::MemoryWriteStreamDynamic thumbStream(DisposeAfterUse::YES);
+ if (_gameRef->_cachedThumbnail->_thumbnail->writeBMPToStream(&thumbStream)) {
+ _saveStream->writeUint32LE(thumbStream.size());
+ _saveStream->write(thumbStream.getData(), thumbStream.size());
+ } else {
+ _saveStream->writeUint32LE(0);
+ }
+
+ thumbnailOK = true;
+ }
+ }
+ if (!thumbnailOK) {
+ putDWORD(0);
+ }
+
+ // in any case, destroy the cached thumbnail once used
+ delete _gameRef->_cachedThumbnail;
+ _gameRef->_cachedThumbnail = NULL;
+
+ uint32 dataOffset = _offset +
+ sizeof(uint32) + // data offset
+ sizeof(uint32) + strlen(desc) + 1 + // description
+ sizeof(uint32); // timestamp
+
+ putDWORD(dataOffset);
+ putString(desc);
+
+ g_system->getTimeAndDate(_savedTimestamp);
+ putTimeDate(_savedTimestamp);
+ _savedPlayTime = g_system->getMillis();
+ _saveStream->writeUint32LE(_savedPlayTime);
+ }
+ return STATUS_OK;
+}
+
+bool BasePersistenceManager::readHeader(const Common::String &filename) {
+ cleanup();
+
+ _saving = false;
+
+ _loadStream = g_system->getSavefileManager()->openForLoading(filename);
+ //_buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &_bufferSize);
+ if (_loadStream) {
+ uint32 magic;
+ magic = getDWORD();
+
+ if (magic != DCGF_MAGIC) {
+ cleanup();
+ return STATUS_FAILED;
+ }
+
+ magic = getDWORD();
+
+ if (magic == SAVE_MAGIC || magic == SAVE_MAGIC_2) {
+ _savedVerMajor = _loadStream->readByte();
+ _savedVerMinor = _loadStream->readByte();
+ _savedExtMajor = _loadStream->readByte();
+ _savedExtMinor = _loadStream->readByte();
+
+ if (magic == SAVE_MAGIC_2) {
+ _savedVerBuild = (byte)getDWORD();
+ _savedName = getStringObj();
+
+ // load thumbnail
+ _thumbnailDataSize = getDWORD();
+ if (_thumbnailDataSize > 0) {
+ _thumbnailData = new byte[_thumbnailDataSize];
+ if (_thumbnailData) {
+ getBytes(_thumbnailData, _thumbnailDataSize);
+ } else {
+ _thumbnailDataSize = 0;
+ }
+ }
+ } else {
+ _savedVerBuild = 35; // last build with ver1 savegames
+ }
+
+ uint32 dataOffset = getDWORD();
+
+ _savedDescription = getString();
+ _savedTimestamp = getTimeDate();
+ _savedPlayTime = _loadStream->readUint32LE();
+
+ _offset = dataOffset;
+
+ return STATUS_OK;
+ }
+ }
+
+ cleanup();
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePersistenceManager::initLoad(const Common::String &filename) {
+ if (DID_FAIL(readHeader(filename))) {
+ cleanup();
+ return STATUS_FAILED;
+ }
+ _saving = false;
+
+ if (_savedName == "" || scumm_stricmp(_savedName.c_str(), _gameRef->getName()) != 0) {
+ debugC(kWintermuteDebugSaveGame, "ERROR: Saved game name doesn't match current game");
+ cleanup();
+ return STATUS_FAILED;
+ }
+
+ // if save is newer version than we are, fail
+ if (_savedVerMajor > DCGF_VER_MAJOR ||
+ (_savedVerMajor == DCGF_VER_MAJOR && _savedVerMinor > DCGF_VER_MINOR) ||
+ (_savedVerMajor == DCGF_VER_MAJOR && _savedVerMinor == DCGF_VER_MINOR && _savedVerBuild > DCGF_VER_BUILD)
+ ) {
+
+ debugC(kWintermuteDebugSaveGame, "ERROR: Saved game version is newer than current game");
+ debugC(kWintermuteDebugSaveGame, "ERROR: Expected %d.%d.%d got %d.%d.%d", DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, _savedVerMajor, _savedVerMinor, _savedVerBuild);
+ cleanup();
+ return STATUS_FAILED;
+ }
+
+ // if save is older than the minimal version we support
+ if (_savedVerMajor < SAVEGAME_VER_MAJOR ||
+ (_savedVerMajor == SAVEGAME_VER_MAJOR && _savedVerMinor < SAVEGAME_VER_MINOR) ||
+ (_savedVerMajor == SAVEGAME_VER_MAJOR && _savedVerMinor == SAVEGAME_VER_MINOR && _savedVerBuild < SAVEGAME_VER_BUILD)
+ ) {
+ debugC(kWintermuteDebugSaveGame, "ERROR: Saved game is too old and cannot be used by this version of game engine");
+ debugC(kWintermuteDebugSaveGame, "ERROR: Expected %d.%d.%d got %d.%d.%d", DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, _savedVerMajor, _savedVerMinor, _savedVerBuild);
+ cleanup();
+ return STATUS_FAILED;
+
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePersistenceManager::saveFile(const Common::String &filename) {
+ byte *prefixBuffer = _richBuffer;
+ uint32 prefixSize = _richBufferSize;
+ byte *buffer = ((Common::MemoryWriteStreamDynamic *)_saveStream)->getData();
+ uint32 bufferSize = ((Common::MemoryWriteStreamDynamic *)_saveStream)->size();
+
+ Common::SaveFileManager *saveMan = ((WintermuteEngine *)g_engine)->getSaveFileMan();
+ Common::OutSaveFile *file = saveMan->openForSaving(filename);
+ file->write(prefixBuffer, prefixSize);
+ file->write(buffer, bufferSize);
+ bool retVal = !file->err();
+ file->finalize();
+ delete file;
+ return retVal;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePersistenceManager::putBytes(byte *buffer, uint32 size) {
+ _saveStream->write(buffer, size);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePersistenceManager::getBytes(byte *buffer, uint32 size) {
+ _loadStream->read(buffer, size);
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BasePersistenceManager::putDWORD(uint32 val) {
+ _saveStream->writeUint32LE(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BasePersistenceManager::getDWORD() {
+ uint32 ret = _loadStream->readUint32LE();
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BasePersistenceManager::putString(const Common::String &val) {
+ if (!val.size()) {
+ putString("(null)");
+ } else {
+ _saveStream->writeUint32LE(val.size());
+ _saveStream->writeString(val);
+ }
+}
+
+Common::String BasePersistenceManager::getStringObj() {
+ uint32 len = _loadStream->readUint32LE();
+ char *ret = new char[len + 1];
+ _loadStream->read(ret, len);
+ ret[len] = '\0';
+
+ Common::String retString = ret;
+ delete[] ret;
+
+ if (retString == "(null)") {
+ retString = "";
+ }
+
+ return retString;
+}
+
+//////////////////////////////////////////////////////////////////////////
+char *BasePersistenceManager::getString() {
+ uint32 len = _loadStream->readUint32LE();
+ char *ret = new char[len + 1];
+ _loadStream->read(ret, len);
+ ret[len] = '\0';
+
+ if (!strcmp(ret, "(null)")) {
+ delete[] ret;
+ return NULL;
+ } else {
+ return ret;
+ }
+}
+
+bool BasePersistenceManager::putTimeDate(const TimeDate &t) {
+ _saveStream->writeSint32LE(t.tm_sec);
+ _saveStream->writeSint32LE(t.tm_min);
+ _saveStream->writeSint32LE(t.tm_hour);
+ _saveStream->writeSint32LE(t.tm_mday);
+ _saveStream->writeSint32LE(t.tm_mon);
+ _saveStream->writeSint32LE(t.tm_year);
+ // _saveStream->writeSint32LE(t.tm_wday); //TODO: Add this in when merging next
+
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+}
+
+TimeDate BasePersistenceManager::getTimeDate() {
+ TimeDate t;
+ t.tm_sec = _loadStream->readSint32LE();
+ t.tm_min = _loadStream->readSint32LE();
+ t.tm_hour = _loadStream->readSint32LE();
+ t.tm_mday = _loadStream->readSint32LE();
+ t.tm_mon = _loadStream->readSint32LE();
+ t.tm_year = _loadStream->readSint32LE();
+ // t.tm_wday = _loadStream->readSint32LE(); //TODO: Add this in when merging next
+ return t;
+}
+
+void BasePersistenceManager::putFloat(float val) {
+ Common::String str = Common::String::format("F%f", val);
+ _saveStream->writeUint32LE(str.size());
+ _saveStream->writeString(str);
+}
+
+float BasePersistenceManager::getFloat() {
+ char *str = getString();
+ float value = 0.0f;
+ int ret = sscanf(str, "F%f", &value);
+ if (ret != 1) {
+ warning("%s not parsed as float", str);
+ }
+ delete[] str;
+ return value;
+}
+
+void BasePersistenceManager::putDouble(double val) {
+ Common::String str = Common::String::format("D%f", val);
+ str.format("D%f", val);
+ _saveStream->writeUint32LE(str.size());
+ _saveStream->writeString(str);
+}
+
+double BasePersistenceManager::getDouble() {
+ char *str = getString();
+ float value = 0.0f; // TODO: Do we ever really need to carry a full double-precision number?
+ int ret = sscanf(str, "D%f", &value);
+ if (ret != 1) {
+ warning("%s not parsed as double", str);
+ }
+ delete[] str;
+ return value;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// bool
+bool BasePersistenceManager::transfer(const char *name, bool *val) {
+ if (_saving) {
+ _saveStream->writeByte(*val);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ *val = _loadStream->readByte();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// int
+bool BasePersistenceManager::transfer(const char *name, int *val) {
+ if (_saving) {
+ _saveStream->writeSint32LE(*val);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ *val = _loadStream->readSint32LE();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// DWORD
+bool BasePersistenceManager::transfer(const char *name, uint32 *val) {
+ if (_saving) {
+ _saveStream->writeUint32LE(*val);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ *val = _loadStream->readUint32LE();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// float
+bool BasePersistenceManager::transfer(const char *name, float *val) {
+ if (_saving) {
+ putFloat(*val);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ *val = getFloat();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// double
+bool BasePersistenceManager::transfer(const char *name, double *val) {
+ if (_saving) {
+ putDouble(*val);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ *val = getDouble();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// char*
+bool BasePersistenceManager::transfer(const char *name, char **val) {
+ if (_saving) {
+ putString(*val);
+ return STATUS_OK;
+ } else {
+ char *str = getString();
+ if (_loadStream->err()) {
+ delete[] str;
+ return STATUS_FAILED;
+ }
+ *val = str;
+ return STATUS_OK;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// const char*
+bool BasePersistenceManager::transfer(const char *name, const char **val) {
+ if (_saving) {
+ putString(*val);
+ return STATUS_OK;
+ } else {
+ char *str = getString();
+ if (_loadStream->err()) {
+ delete[] str;
+ return STATUS_FAILED;
+ }
+ *val = str;
+ return STATUS_OK;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Common::String
+bool BasePersistenceManager::transfer(const char *name, Common::String *val) {
+ if (_saving) {
+ putString(*val);
+ return STATUS_OK;
+ } else {
+ char *str = getString();
+ if (_loadStream->err()) {
+ delete[] str;
+ return STATUS_FAILED;
+ }
+ if (str) {
+ *val = str;
+ delete[] str;
+ } else {
+ *val = "";
+ }
+ return STATUS_OK;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePersistenceManager::transfer(const char *name, AnsiStringArray &val) {
+ size_t size;
+
+ if (_saving) {
+ size = val.size();
+ _saveStream->writeUint32LE(size);
+
+ for (AnsiStringArray::iterator it = val.begin(); it != val.end(); ++it) {
+ putString((*it).c_str());
+ }
+ } else {
+ val.clear();
+ size = _loadStream->readUint32LE();
+
+ for (size_t i = 0; i < size; i++) {
+ char *str = getString();
+ if (_loadStream->err()) {
+ delete[] str;
+ return STATUS_FAILED;
+ }
+ if (str) {
+ val.push_back(str);
+ }
+ delete[] str;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// BYTE
+bool BasePersistenceManager::transfer(const char *name, byte *val) {
+ if (_saving) {
+ _saveStream->writeByte(*val);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ *val = _loadStream->readByte();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// RECT
+bool BasePersistenceManager::transfer(const char *name, Rect32 *val) {
+ if (_saving) {
+ _saveStream->writeSint32LE(val->left);
+ _saveStream->writeSint32LE(val->top);
+ _saveStream->writeSint32LE(val->right);
+ _saveStream->writeSint32LE(val->bottom);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ val->left = _loadStream->readSint32LE();
+ val->top = _loadStream->readSint32LE();
+ val->right = _loadStream->readSint32LE();
+ val->bottom = _loadStream->readSint32LE();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// POINT
+bool BasePersistenceManager::transfer(const char *name, Point32 *val) {
+ if (_saving) {
+ _saveStream->writeSint32LE(val->x);
+ _saveStream->writeSint32LE(val->y);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ val->x = _loadStream->readSint32LE();
+ val->y = _loadStream->readSint32LE();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Vector2
+bool BasePersistenceManager::transfer(const char *name, Vector2 *val) {
+ if (_saving) {
+ putFloat(val->x);
+ putFloat(val->y);
+ if (_saveStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ } else {
+ val->x = getFloat();
+ val->y = getFloat();
+ if (_loadStream->err()) {
+ return STATUS_FAILED;
+ }
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// generic pointer
+bool BasePersistenceManager::transfer(const char *name, void *val) {
+ int classID = -1, instanceID = -1;
+
+ if (_saving) {
+ SystemClassRegistry::getInstance()->getPointerID(*(void **)val, &classID, &instanceID);
+ if (*(void **)val != NULL && (classID == -1 || instanceID == -1)) {
+ debugC(kWintermuteDebugSaveGame, "Warning: invalid instance '%s'", name);
+ }
+
+ _saveStream->writeUint32LE(classID);
+ _saveStream->writeUint32LE(instanceID);
+ } else {
+ classID = _loadStream->readUint32LE();
+ instanceID = _loadStream->readUint32LE();
+
+ *(void **)val = SystemClassRegistry::getInstance()->idToPointer(classID, instanceID);
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePersistenceManager::checkVersion(byte verMajor, byte verMinor, byte verBuild) {
+ if (_saving) {
+ return true;
+ }
+
+ // it's ok if we are same or newer than the saved game
+ if (verMajor > _savedVerMajor ||
+ (verMajor == _savedVerMajor && verMinor > _savedVerMinor) ||
+ (verMajor == _savedVerMajor && verMinor == _savedVerMinor && verBuild > _savedVerBuild)
+ ) {
+ return false;
+ }
+
+ return true;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_persistence_manager.h b/engines/wintermute/base/base_persistence_manager.h
new file mode 100644
index 0000000000..a262c92a0b
--- /dev/null
+++ b/engines/wintermute/base/base_persistence_manager.h
@@ -0,0 +1,118 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_PERSISTENCE_MANAGER_H
+#define WINTERMUTE_BASE_PERSISTENCE_MANAGER_H
+
+
+#include "engines/wintermute/dctypes.h"
+#include "engines/wintermute/math/rect32.h"
+#include "engines/savestate.h"
+#include "common/stream.h"
+#include "common/str.h"
+#include "common/system.h"
+#include "common/rect.h"
+
+namespace Wintermute {
+
+class Vector2;
+class BaseGame;
+class BasePersistenceManager {
+public:
+ char *_savedDescription;
+ Common::String _savePrefix;
+ Common::String _savedName;
+ bool saveFile(const Common::String &filename);
+ uint32 getDWORD();
+ void putDWORD(uint32 val);
+ char *getString();
+ Common::String getStringObj();
+ void putString(const Common::String &val);
+ float getFloat();
+ void putFloat(float val);
+ double getDouble();
+ void putDouble(double val);
+ void cleanup();
+ void getSaveStateDesc(int slot, SaveStateDescriptor &desc);
+ void deleteSaveSlot(int slot);
+ uint32 getMaxUsedSlot();
+ bool getSaveExists(int slot);
+ bool initLoad(const Common::String &filename);
+ bool initSave(const char *desc);
+ bool getBytes(byte *buffer, uint32 size);
+ bool putBytes(byte *buffer, uint32 size);
+ uint32 _offset;
+
+ bool getIsSaving() { return _saving; }
+
+ uint32 _richBufferSize;
+ byte *_richBuffer;
+
+ bool transfer(const char *name, void *val);
+ bool transfer(const char *name, int *val);
+ bool transfer(const char *name, uint32 *val);
+ bool transfer(const char *name, float *val);
+ bool transfer(const char *name, double *val);
+ bool transfer(const char *name, bool *val);
+ bool transfer(const char *name, byte *val);
+ bool transfer(const char *name, Rect32 *val);
+ bool transfer(const char *name, Point32 *val);
+ bool transfer(const char *name, const char **val);
+ bool transfer(const char *name, char **val);
+ bool transfer(const char *name, Common::String *val);
+ bool transfer(const char *name, Vector2 *val);
+ bool transfer(const char *name, AnsiStringArray &Val);
+ BasePersistenceManager(const char *savePrefix = NULL, bool deleteSingleton = false);
+ virtual ~BasePersistenceManager();
+ bool checkVersion(byte verMajor, byte verMinor, byte verBuild);
+
+ uint32 _thumbnailDataSize;
+ byte *_thumbnailData;
+ Common::String getFilenameForSlot(int slot) const;
+private:
+ bool _deleteSingleton;
+ bool readHeader(const Common::String &filename);
+ TimeDate getTimeDate();
+ bool putTimeDate(const TimeDate &t);
+ Common::WriteStream *_saveStream;
+ Common::SeekableReadStream *_loadStream;
+ TimeDate _savedTimestamp;
+ uint32 _savedPlayTime;
+ byte _savedVerMajor;
+ byte _savedVerMinor;
+ byte _savedVerBuild;
+ byte _savedExtMajor;
+ byte _savedExtMinor;
+ bool _saving;
+ // Separate from Base, as this class can do SOME operations without a _gameRef.
+ BaseGame *_gameRef;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_point.cpp b/engines/wintermute/base/base_point.cpp
new file mode 100644
index 0000000000..fbd8960894
--- /dev/null
+++ b/engines/wintermute/base/base_point.cpp
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_point.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BasePoint, false)
+
+//////////////////////////////////////////////////////////////////////////
+BasePoint::BasePoint() {
+ x = y = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BasePoint::~BasePoint() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BasePoint::BasePoint(int initX, int initY) {
+ x = initX;
+ y = initY;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePoint::persist(BasePersistenceManager *persistMgr) {
+
+ persistMgr->transfer(TMEMBER(x));
+ persistMgr->transfer(TMEMBER(y));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_point.h b/engines/wintermute/base/base_point.h
new file mode 100644
index 0000000000..c0bbd3102e
--- /dev/null
+++ b/engines/wintermute/base/base_point.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_POINT_H
+#define WINTERMUTE_BASE_POINT_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+class BasePoint: public BaseClass {
+public:
+ DECLARE_PERSISTENT(BasePoint, BaseClass)
+ BasePoint();
+ BasePoint(int initX, int initY);
+ int y;
+ int x;
+ virtual ~BasePoint();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_quick_msg.cpp b/engines/wintermute/base/base_quick_msg.cpp
new file mode 100644
index 0000000000..0a9907ac6b
--- /dev/null
+++ b/engines/wintermute/base/base_quick_msg.cpp
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_quick_msg.h"
+#include "engines/wintermute/base/base_game.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+BaseQuickMsg::BaseQuickMsg(BaseGame *inGame, const char *text) : BaseClass(inGame) {
+ _text = new char [strlen(text) + 1];
+ if (_text) {
+ strcpy(_text, text);
+ }
+ _startTime = _gameRef->_currentTime;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseQuickMsg::~BaseQuickMsg() {
+ if (_text) {
+ delete[] _text;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *BaseQuickMsg::getText() {
+ return _text;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_quick_msg.h b/engines/wintermute/base/base_quick_msg.h
new file mode 100644
index 0000000000..67f9613461
--- /dev/null
+++ b/engines/wintermute/base/base_quick_msg.h
@@ -0,0 +1,48 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_QUICKMSG_H
+#define WINTERMUTE_BASE_QUICKMSG_H
+
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+class BaseQuickMsg : public BaseClass {
+public:
+ char *getText();
+ uint32 _startTime;
+ BaseQuickMsg(BaseGame *inGame, const char *text);
+ virtual ~BaseQuickMsg();
+private:
+ char *_text;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_region.cpp b/engines/wintermute/base/base_region.cpp
new file mode 100644
index 0000000000..0bc5975e51
--- /dev/null
+++ b/engines/wintermute/base/base_region.cpp
@@ -0,0 +1,535 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/platform_osystem.h"
+#include <limits.h>
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseRegion, false)
+
+//////////////////////////////////////////////////////////////////////////
+BaseRegion::BaseRegion(BaseGame *inGame) : BaseObject(inGame) {
+ _active = true;
+ _editorSelectedPoint = -1;
+ _lastMimicScale = -1;
+ _lastMimicX = _lastMimicY = INT_MIN;
+
+ BasePlatform::setRectEmpty(&_rect);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseRegion::~BaseRegion() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseRegion::cleanup() {
+ for (uint32 i = 0; i < _points.size(); i++) {
+ delete _points[i];
+ }
+ _points.clear();
+
+ BasePlatform::setRectEmpty(&_rect);
+ _editorSelectedPoint = -1;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::createRegion() {
+ return DID_SUCCEED(getBoundingRect(&_rect));
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::pointInRegion(int x, int y) {
+ if (_points.size() < 3) {
+ return false;
+ }
+
+ Point32 pt;
+ pt.x = x;
+ pt.y = y;
+
+ Rect32 rect;
+ rect.left = x - 1;
+ rect.right = x + 2;
+ rect.top = y - 1;
+ rect.bottom = y + 2;
+
+ if (BasePlatform::ptInRect(&_rect, pt)) {
+ return ptInPolygon(x, y);
+ } else {
+ return false;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "BaseRegion::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing REGION file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(REGION)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(NAME)
+TOKEN_DEF(ACTIVE)
+TOKEN_DEF(POINT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(EDITOR_SELECTED_POINT)
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(REGION)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(ACTIVE)
+ TOKEN_TABLE(POINT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(EDITOR_SELECTED_POINT)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_REGION) {
+ _gameRef->LOG(0, "'REGION' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ for (uint32 i = 0; i < _points.size(); i++) {
+ delete _points[i];
+ }
+ _points.clear();
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_ACTIVE:
+ parser.scanStr((char *)params, "%b", &_active);
+ break;
+
+ case TOKEN_POINT: {
+ int x, y;
+ parser.scanStr((char *)params, "%d,%d", &x, &y);
+ _points.add(new BasePoint(x, y));
+ }
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_EDITOR_SELECTED_POINT:
+ parser.scanStr((char *)params, "%d", &_editorSelectedPoint);
+ break;
+
+ case TOKEN_PROPERTY:
+ parseProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in REGION definition");
+ return STATUS_FAILED;
+ }
+
+ createRegion();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddPoint
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "AddPoint") == 0) {
+ stack->correctParams(2);
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+
+ _points.add(new BasePoint(x, y));
+ createRegion();
+
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InsertPoint
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InsertPoint") == 0) {
+ stack->correctParams(3);
+ int index = stack->pop()->getInt();
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+
+ if (index >= 0 && index < (int32)_points.size()) {
+ _points.insert_at(index, new BasePoint(x, y));
+ createRegion();
+
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetPoint
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetPoint") == 0) {
+ stack->correctParams(3);
+ int index = stack->pop()->getInt();
+ int x = stack->pop()->getInt();
+ int y = stack->pop()->getInt();
+
+ if (index >= 0 && index < (int32)_points.size()) {
+ _points[index]->x = x;
+ _points[index]->y = y;
+ createRegion();
+
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RemovePoint
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemovePoint") == 0) {
+ stack->correctParams(1);
+ int index = stack->pop()->getInt();
+
+ if (index >= 0 && index < (int32)_points.size()) {
+ delete _points[index];
+ _points[index] = NULL;
+
+ _points.remove_at(index);
+ createRegion();
+
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetPoint
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetPoint") == 0) {
+ stack->correctParams(1);
+ int index = stack->pop()->getInt();
+
+ if (index >= 0 && index < (int32)_points.size()) {
+ ScValue *val = stack->getPushValue();
+ if (val) {
+ val->setProperty("X", _points[index]->x);
+ val->setProperty("Y", _points[index]->y);
+ }
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ } else {
+ return BaseObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseRegion::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("region");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Name") {
+ _scValue->setString(getName());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Active
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Active") {
+ _scValue->setBool(_active);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumPoints
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumPoints") {
+ _scValue->setInt(_points.size());
+ return _scValue;
+ } else {
+ return BaseObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Active
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Active") == 0) {
+ _active = value->getBool();
+ return STATUS_OK;
+ } else {
+ return BaseObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseRegion::scToString() {
+ return "[region]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::saveAsText(BaseDynamicBuffer *buffer, int indent, const char *nameOverride) {
+ if (!nameOverride) {
+ buffer->putTextIndent(indent, "REGION {\n");
+ } else {
+ buffer->putTextIndent(indent, "%s {\n", nameOverride);
+ }
+
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+ buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "EDITOR_SELECTED_POINT=%d\n", _editorSelectedPoint);
+
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ for (uint32 i = 0; i < _points.size(); i++) {
+ buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y);
+ }
+
+ if (_scProp) {
+ _scProp->saveAsText(buffer, indent + 2);
+ }
+
+ buffer->putTextIndent(indent, "}\n\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_active));
+ persistMgr->transfer(TMEMBER(_editorSelectedPoint));
+ persistMgr->transfer(TMEMBER(_lastMimicScale));
+ persistMgr->transfer(TMEMBER(_lastMimicX));
+ persistMgr->transfer(TMEMBER(_lastMimicY));
+ _points.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+typedef struct {
+ double x, y;
+} dPoint;
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::ptInPolygon(int x, int y) {
+ if (_points.size() < 3) {
+ return false;
+ }
+
+ int counter = 0;
+ double xinters;
+ dPoint p, p1, p2;
+
+ p.x = (double)x;
+ p.y = (double)y;
+
+ p1.x = (double)_points[0]->x;
+ p1.y = (double)_points[0]->y;
+
+ for (uint32 i = 1; i <= _points.size(); i++) {
+ p2.x = (double)_points[i % _points.size()]->x;
+ p2.y = (double)_points[i % _points.size()]->y;
+
+ if (p.y > MIN(p1.y, p2.y)) {
+ if (p.y <= MAX(p1.y, p2.y)) {
+ if (p.x <= MAX(p1.x, p2.x)) {
+ if (p1.y != p2.y) {
+ xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
+ if (p1.x == p2.x || p.x <= xinters) {
+ counter++;
+ }
+ }
+ }
+ }
+ }
+ p1 = p2;
+ }
+
+ if (counter % 2 == 0) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::getBoundingRect(Rect32 *rect) {
+ if (_points.size() == 0) {
+ BasePlatform::setRectEmpty(rect);
+ } else {
+ int minX = INT_MAX, minY = INT_MAX, maxX = INT_MIN, maxY = INT_MIN;
+
+ for (uint32 i = 0; i < _points.size(); i++) {
+ minX = MIN(minX, _points[i]->x);
+ minY = MIN(minY, _points[i]->y);
+
+ maxX = MAX(maxX, _points[i]->x);
+ maxY = MAX(maxY, _points[i]->y);
+ }
+ BasePlatform::setRect(rect, minX, minY, maxX, maxY);
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRegion::mimic(BaseRegion *region, float scale, int x, int y) {
+ if (scale == _lastMimicScale && x == _lastMimicX && y == _lastMimicY) {
+ return STATUS_OK;
+ }
+
+ cleanup();
+
+ for (uint32 i = 0; i < region->_points.size(); i++) {
+ int xVal, yVal;
+
+ xVal = (int)((float)region->_points[i]->x * scale / 100.0f);
+ yVal = (int)((float)region->_points[i]->y * scale / 100.0f);
+
+ _points.add(new BasePoint(xVal + x, yVal + y));
+ }
+
+ _lastMimicScale = scale;
+ _lastMimicX = x;
+ _lastMimicY = y;
+
+ return createRegion() ? STATUS_OK : STATUS_FAILED;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_region.h b/engines/wintermute/base/base_region.h
new file mode 100644
index 0000000000..464f25be2f
--- /dev/null
+++ b/engines/wintermute/base/base_region.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_REGION_H
+#define WINTERMUTE_BASE_REGION_H
+
+#include "engines/wintermute/base/base_point.h"
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+
+class BaseRegion : public BaseObject {
+public:
+ void cleanup();
+ bool mimic(BaseRegion *region, float scale = 100.0f, int x = 0, int y = 0);
+ bool getBoundingRect(Rect32 *rect);
+ bool ptInPolygon(int x, int y);
+ DECLARE_PERSISTENT(BaseRegion, BaseObject)
+ bool _active;
+ int _editorSelectedPoint;
+ BaseRegion(BaseGame *inGame);
+ virtual ~BaseRegion();
+ bool pointInRegion(int x, int y);
+ bool createRegion();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ Rect32 _rect;
+ BaseArray<BasePoint *> _points;
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent) { return saveAsText(buffer, indent, NULL); }
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent, const char *nameOverride);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+private:
+ float _lastMimicScale;
+ int _lastMimicX;
+ int _lastMimicY;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_save_thumb_helper.cpp b/engines/wintermute/base/base_save_thumb_helper.cpp
new file mode 100644
index 0000000000..b4205c21c4
--- /dev/null
+++ b/engines/wintermute/base/base_save_thumb_helper.cpp
@@ -0,0 +1,81 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_save_thumb_helper.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/base_game.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+BaseSaveThumbHelper::BaseSaveThumbHelper(BaseGame *inGame) : BaseClass(inGame) {
+ _thumbnail = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseSaveThumbHelper::~BaseSaveThumbHelper(void) {
+ delete _thumbnail;
+ _thumbnail = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSaveThumbHelper::storeThumbnail(bool doFlip) {
+ delete _thumbnail;
+ _thumbnail = NULL;
+
+ if (_gameRef->_thumbnailWidth > 0 && _gameRef->_thumbnailHeight > 0) {
+ if (doFlip) {
+ // when using opengl on windows it seems to be necessary to do this twice
+ // works normally for direct3d
+ _gameRef->displayContent(false);
+ _gameRef->_renderer->flip();
+
+ _gameRef->displayContent(false);
+ _gameRef->_renderer->flip();
+ }
+
+ BaseImage *screenshot = _gameRef->_renderer->takeScreenshot();
+ if (!screenshot) {
+ return STATUS_FAILED;
+ }
+
+ // normal thumbnail
+ if (_gameRef->_thumbnailWidth > 0 && _gameRef->_thumbnailHeight > 0) {
+ _thumbnail = new BaseImage();
+ _thumbnail->copyFrom(screenshot, _gameRef->_thumbnailWidth, _gameRef->_thumbnailHeight);
+ }
+
+
+ delete screenshot;
+ screenshot = NULL;
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_save_thumb_helper.h b/engines/wintermute/base/base_save_thumb_helper.h
new file mode 100644
index 0000000000..8863508ac9
--- /dev/null
+++ b/engines/wintermute/base/base_save_thumb_helper.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+#ifndef WINTERMUTE_BASE_SAVE_THUMB_HELPER_H
+#define WINTERMUTE_BASE_SAVE_THUMB_HELPER_H
+
+
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+class BaseImage;
+
+class BaseSaveThumbHelper : public BaseClass {
+public:
+ BaseSaveThumbHelper(BaseGame *inGame);
+ virtual ~BaseSaveThumbHelper(void);
+ bool storeThumbnail(bool doFlip = false);
+
+ BaseImage *_thumbnail;
+private:
+ BaseImage *_richThumbnail;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_script_holder.cpp b/engines/wintermute/base/base_script_holder.cpp
new file mode 100644
index 0000000000..c5d5e82f76
--- /dev/null
+++ b/engines/wintermute/base/base_script_holder.cpp
@@ -0,0 +1,502 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/base/base_script_holder.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_engine.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseScriptHolder, false)
+
+//////////////////////////////////////////////////////////////////////
+BaseScriptHolder::BaseScriptHolder(BaseGame *inGame) : BaseScriptable(inGame) {
+ setName("<unnamed>");
+
+ _freezable = true;
+ _filename = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseScriptHolder::~BaseScriptHolder() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::cleanup() {
+ delete[] _filename;
+ _filename = NULL;
+
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ _scripts[i]->finish(true);
+ _scripts[i]->_owner = NULL;
+ }
+ _scripts.clear();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////
+void BaseScriptHolder::setFilename(const char *filename) {
+ if (_filename != NULL) {
+ delete[] _filename;
+ _filename = NULL;
+ }
+ if (filename == NULL) {
+ return;
+ }
+ _filename = new char [strlen(filename) + 1];
+ if (_filename != NULL) {
+ strcpy(_filename, filename);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::applyEvent(const char *eventName, bool unbreakable) {
+ int numHandlers = 0;
+
+ bool ret = STATUS_FAILED;
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (!_scripts[i]->_thread) {
+ ScScript *handler = _scripts[i]->invokeEventHandler(eventName, unbreakable);
+ if (handler) {
+ //_scripts.add(handler);
+ numHandlers++;
+ ret = STATUS_OK;
+ }
+ }
+ }
+ if (numHandlers > 0 && unbreakable) {
+ _gameRef->_scEngine->tickUnbreakable();
+ }
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::listen(BaseScriptHolder *param1, uint32 param2) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // DEBUG_CrashMe
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "DEBUG_CrashMe") == 0) {
+ stack->correctParams(0);
+ byte *p = 0;
+ *p = 10;
+ stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ApplyEvent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ApplyEvent") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ bool ret;
+ ret = applyEvent(val->getString());
+
+ if (DID_SUCCEED(ret)) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CanHandleEvent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CanHandleEvent") == 0) {
+ stack->correctParams(1);
+ stack->pushBool(canHandleEvent(stack->pop()->getString()));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CanHandleMethod
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CanHandleMethod") == 0) {
+ stack->correctParams(1);
+ stack->pushBool(canHandleMethod(stack->pop()->getString()));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AttachScript
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AttachScript") == 0) {
+ stack->correctParams(1);
+ stack->pushBool(DID_SUCCEED(addScript(stack->pop()->getString())));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DetachScript
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DetachScript") == 0) {
+ stack->correctParams(2);
+ const char *filename = stack->pop()->getString();
+ bool killThreads = stack->pop()->getBool(false);
+ bool ret = false;
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (scumm_stricmp(_scripts[i]->_filename, filename) == 0) {
+ _scripts[i]->finish(killThreads);
+ ret = true;
+ break;
+ }
+ }
+ stack->pushBool(ret);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsScriptRunning
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsScriptRunning") == 0) {
+ stack->correctParams(1);
+ const char *filename = stack->pop()->getString();
+ bool ret = false;
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (scumm_stricmp(_scripts[i]->_filename, filename) == 0 && _scripts[i]->_state != SCRIPT_FINISHED && _scripts[i]->_state != SCRIPT_ERROR) {
+ ret = true;
+ break;
+ }
+ }
+ stack->pushBool(ret);
+
+ return STATUS_OK;
+ } else {
+ return BaseScriptable::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseScriptHolder::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("script_holder");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Name") {
+ _scValue->setString(getName());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Filename (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Filename") {
+ _scValue->setString(_filename);
+ return _scValue;
+ } else {
+ return BaseScriptable::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ return STATUS_OK;
+ } else {
+ return BaseScriptable::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseScriptHolder::scToString() {
+ return "[script_holder]";
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ return BaseClass::saveAsText(buffer, indent);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::persist(BasePersistenceManager *persistMgr) {
+ BaseScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_filename));
+ persistMgr->transfer(TMEMBER(_freezable));
+ if (persistMgr->getIsSaving()) {
+ const char *name = getName();
+ persistMgr->transfer(TMEMBER(name));
+ } else {
+ char *name;
+ persistMgr->transfer(TMEMBER(name));
+ setName(name);
+ delete[] name;
+ }
+ _scripts.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::addScript(const char *filename) {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (scumm_stricmp(_scripts[i]->_filename, filename) == 0) {
+ if (_scripts[i]->_state != SCRIPT_FINISHED) {
+ _gameRef->LOG(0, "BaseScriptHolder::AddScript - trying to add script '%s' mutiple times (obj: '%s')", filename, getName());
+ return STATUS_OK;
+ }
+ }
+ }
+
+ ScScript *scr = _gameRef->_scEngine->runScript(filename, this);
+ if (!scr) {
+ if (_gameRef->_editorForceScripts) {
+ // editor hack
+ scr = new ScScript(_gameRef, _gameRef->_scEngine);
+ scr->_filename = new char[strlen(filename) + 1];
+ strcpy(scr->_filename, filename);
+ scr->_state = SCRIPT_ERROR;
+ scr->_owner = this;
+ _scripts.add(scr);
+ _gameRef->_scEngine->_scripts.add(scr);
+
+ return STATUS_OK;
+ }
+ return STATUS_FAILED;
+ } else {
+ scr->_freezable = _freezable;
+ _scripts.add(scr);
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::removeScript(ScScript *script) {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (_scripts[i] == script) {
+ _scripts.remove_at(i);
+ break;
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::canHandleEvent(const char *EventName) {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (!_scripts[i]->_thread && _scripts[i]->canHandleEvent(EventName)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::canHandleMethod(const char *MethodName) {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (!_scripts[i]->_thread && _scripts[i]->canHandleMethod(MethodName)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(PROPERTY)
+TOKEN_DEF(NAME)
+TOKEN_DEF(VALUE)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::parseProperty(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(PROPERTY)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(VALUE)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_PROPERTY) {
+ _gameRef->LOG(0, "'PROPERTY' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ char *propName = NULL;
+ char *propValue = NULL;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_NAME:
+ delete[] propName;
+ propName = new char[strlen((char *)params) + 1];
+ if (propName) {
+ strcpy(propName, (char *)params);
+ } else {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_VALUE:
+ delete[] propValue;
+ propValue = new char[strlen((char *)params) + 1];
+ if (propValue) {
+ strcpy(propValue, (char *)params);
+ } else {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+ }
+
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ delete[] propName;
+ delete[] propValue;
+ propName = NULL;
+ propValue = NULL;
+ _gameRef->LOG(0, "Syntax error in PROPERTY definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC || propName == NULL || propValue == NULL) {
+ delete[] propName;
+ delete[] propValue;
+ propName = NULL;
+ propValue = NULL;
+ _gameRef->LOG(0, "Error loading PROPERTY definition");
+ return STATUS_FAILED;
+ }
+
+
+ ScValue *val = new ScValue(_gameRef);
+ val->setString(propValue);
+ scSetProperty(propName, val);
+
+ delete val;
+ delete[] propName;
+ delete[] propValue;
+ propName = NULL;
+ propValue = NULL;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseScriptHolder::makeFreezable(bool freezable) {
+ _freezable = freezable;
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ _scripts[i]->_freezable = freezable;
+ }
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScScript *BaseScriptHolder::invokeMethodThread(const char *methodName) {
+ for (int i = _scripts.size() - 1; i >= 0; i--) {
+ if (_scripts[i]->canHandleMethod(methodName)) {
+
+ ScScript *thread = new ScScript(_gameRef, _scripts[i]->_engine);
+ if (thread) {
+ bool ret = thread->createMethodThread(_scripts[i], methodName);
+ if (DID_SUCCEED(ret)) {
+ _scripts[i]->_engine->_scripts.add(thread);
+ return thread;
+ } else {
+ delete thread;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseScriptHolder::scDebuggerDesc(char *buf, int bufSize) {
+ strcpy(buf, scToString());
+ if (getName() && strcmp(getName(), "<unnamed>") != 0) {
+ strcat(buf, " Name: ");
+ strcat(buf, getName());
+ }
+ if (_filename) {
+ strcat(buf, " File: ");
+ strcat(buf, _filename);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// IWmeObject
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptHolder::sendEvent(const char *eventName) {
+ return DID_SUCCEED(applyEvent(eventName));
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_script_holder.h b/engines/wintermute/base/base_script_holder.h
new file mode 100644
index 0000000000..5fd0dbec9c
--- /dev/null
+++ b/engines/wintermute/base/base_script_holder.h
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SCRIPTHOLDER_H
+#define WINTERMUTE_BASE_SCRIPTHOLDER_H
+
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/base_scriptable.h"
+
+namespace Wintermute {
+
+class BaseScriptHolder : public BaseScriptable {
+public:
+ DECLARE_PERSISTENT(BaseScriptHolder, BaseScriptable)
+
+ BaseScriptHolder(BaseGame *inGame);
+ virtual ~BaseScriptHolder();
+ virtual ScScript *invokeMethodThread(const char *methodName);
+ virtual void makeFreezable(bool freezable);
+ bool canHandleEvent(const char *eventName);
+ virtual bool canHandleMethod(const char *eventMethod);
+ bool cleanup();
+ bool removeScript(ScScript *script);
+ bool addScript(const char *filename);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ virtual bool listen(BaseScriptHolder *param1, uint32 param2);
+ bool applyEvent(const char *eventName, bool unbreakable = false);
+ void setFilename(const char *filename);
+ const char *getFilename() { return _filename; }
+ bool parseProperty(byte *buffer, bool complete = true);
+ bool _freezable;
+ bool _ready;
+
+ BaseArray<ScScript *> _scripts;
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+ virtual void scDebuggerDesc(char *buf, int bufSize);
+ // IWmeObject
+private:
+ char *_filename;
+public:
+ virtual bool sendEvent(const char *eventName);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_scriptable.cpp b/engines/wintermute/base/base_scriptable.cpp
new file mode 100644
index 0000000000..a2dd8b00e7
--- /dev/null
+++ b/engines/wintermute/base/base_scriptable.cpp
@@ -0,0 +1,191 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_scriptable.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseScriptable, false)
+
+//////////////////////////////////////////////////////////////////////////
+BaseScriptable::BaseScriptable(BaseGame *inGame, bool noValue, bool persistable) : BaseNamedObject(inGame) {
+ _refCount = 0;
+
+ if (noValue) {
+ _scValue = NULL;
+ } else {
+ _scValue = new ScValue(_gameRef);
+ }
+
+ _persistable = persistable;
+
+ _scProp = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseScriptable::~BaseScriptable() {
+ //if (_refCount>0) _gameRef->LOG(0, "Warning: Destroying object, _refCount=%d", _refCount);
+ delete _scValue;
+ delete _scProp;
+ _scValue = NULL;
+ _scProp = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptable::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ /*
+ stack->correctParams(0);
+ stack->pushNULL();
+ script->runtimeError("Call to undefined method '%s'.", name);
+
+ return STATUS_OK;
+ */
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseScriptable::scGetProperty(const Common::String &name) {
+ if (!_scProp) {
+ _scProp = new ScValue(_gameRef);
+ }
+ if (_scProp) {
+ return _scProp->getProp(name.c_str()); // TODO: Change to Common::String
+ } else {
+ return NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptable::scSetProperty(const char *name, ScValue *value) {
+ if (!_scProp) {
+ _scProp = new ScValue(_gameRef);
+ }
+ if (_scProp) {
+ return _scProp->setProp(name, value);
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseScriptable::scToString() {
+ return "[native object]";
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *BaseScriptable::scToMemBuffer() {
+ return (void *)NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseScriptable::scToInt() {
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double BaseScriptable::scToFloat() {
+ return 0.0f;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptable::scToBool() {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseScriptable::scSetString(const char *val) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseScriptable::scSetInt(int val) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseScriptable::scSetFloat(double val) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseScriptable::scSetBool(bool val) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptable::persist(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_gameRef));
+ persistMgr->transfer(TMEMBER(_refCount));
+ persistMgr->transfer(TMEMBER(_scProp));
+ persistMgr->transfer(TMEMBER(_scValue));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseScriptable::scCompare(BaseScriptable *val) {
+ if (this < val) {
+ return -1;
+ } else if (this > val) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseScriptable::scDebuggerDesc(char *buf, int bufSize) {
+ strcpy(buf, scToString());
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseScriptable::canHandleMethod(const char *eventMethod) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScScript *BaseScriptable::invokeMethodThread(const char *methodName) {
+ return NULL;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_scriptable.h b/engines/wintermute/base/base_scriptable.h
new file mode 100644
index 0000000000..fbe14fc299
--- /dev/null
+++ b/engines/wintermute/base/base_scriptable.h
@@ -0,0 +1,83 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SCRIPTABLE_H
+#define WINTERMUTE_BASE_SCRIPTABLE_H
+
+
+#include "engines/wintermute/base/base_named_object.h"
+#include "engines/wintermute/persistent.h"
+
+namespace Wintermute {
+
+class ScValue;
+class ScStack;
+class ScScript;
+
+class BaseScriptable : public BaseNamedObject {
+public:
+ virtual ScScript *invokeMethodThread(const char *methodName);
+ DECLARE_PERSISTENT(BaseScriptable, BaseNamedObject)
+
+ BaseScriptable(BaseGame *inGame, bool noValue = false, bool persistable = true);
+ virtual ~BaseScriptable();
+
+ // high level scripting interface
+ virtual bool canHandleMethod(const char *eventMethod);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+ virtual void *scToMemBuffer();
+ virtual int scToInt();
+ virtual double scToFloat();
+ virtual bool scToBool();
+ virtual void scSetString(const char *val);
+ virtual void scSetInt(int val);
+ virtual void scSetFloat(double val);
+ virtual void scSetBool(bool val);
+ virtual int scCompare(BaseScriptable *val);
+ virtual void scDebuggerDesc(char *buf, int bufSize);
+ int _refCount;
+ ScValue *_scValue;
+ ScValue *_scProp;
+};
+
+// Implemented in their respective .cpp-files
+BaseScriptable *makeSXArray(BaseGame *inGame, ScStack *stack);
+BaseScriptable *makeSXDate(BaseGame *inGame, ScStack *stack);
+BaseScriptable *makeSXFile(BaseGame *inGame, ScStack *stack);
+BaseScriptable *makeSXMath(BaseGame *inGame);
+BaseScriptable *makeSXMemBuffer(BaseGame *inGame, ScStack *stack);
+BaseScriptable *makeSXObject(BaseGame *inGame, ScStack *stack);
+BaseScriptable *makeSXStore(BaseGame *inGame);
+BaseScriptable *makeSXString(BaseGame *inGame, ScStack *stack);
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_sprite.cpp b/engines/wintermute/base/base_sprite.cpp
new file mode 100644
index 0000000000..468af1bd75
--- /dev/null
+++ b/engines/wintermute/base/base_sprite.cpp
@@ -0,0 +1,819 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/utils/path_util.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_frame.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseSprite, false)
+
+//////////////////////////////////////////////////////////////////////
+BaseSprite::BaseSprite(BaseGame *inGame, BaseObject *Owner) : BaseScriptHolder(inGame) {
+ _editorAllFrames = false;
+ _owner = Owner;
+ setDefaults();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseSprite::~BaseSprite() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSprite::setDefaults() {
+ _currentFrame = -1;
+ _looping = false;
+ _lastFrameTime = 0;
+ setFilename(NULL);
+ _finished = false;
+ _changed = false;
+ _paused = false;
+ _continuous = false;
+ _moveX = _moveY = 0;
+
+ _editorMuted = false;
+ _editorBgFile = NULL;
+ _editorBgOffsetX = _editorBgOffsetY = 0;
+ _editorBgAlpha = 0xFF;
+ _streamed = false;
+ _streamedKeepLoaded = false;
+
+ setName("");
+
+ _precise = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSprite::cleanup() {
+ BaseScriptHolder::cleanup();
+
+ for (uint32 i = 0; i < _frames.size(); i++) {
+ delete _frames[i];
+ }
+ _frames.clear();
+
+ delete[] _editorBgFile;
+ _editorBgFile = NULL;
+
+ setDefaults();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSprite::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, uint32 alpha) {
+ getCurrentFrame(zoomX, zoomY);
+ if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) {
+ return STATUS_OK;
+ }
+
+ // move owner if allowed to
+ if (_changed && _owner && _owner->_movable) {
+ _owner->_posX += _moveX;
+ _owner->_posY += _moveY;
+ _owner->afterMove();
+
+ x = _owner->_posX;
+ y = _owner->_posY;
+ }
+
+ // draw frame
+ return display(x, y, registerOwner, zoomX, zoomY, alpha);
+}
+
+bool BaseSprite::isChanged() {
+ return _changed;
+}
+
+bool BaseSprite::isFinished() {
+ return _finished;
+}
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSprite::loadFile(const Common::String &filename, int lifeTime, TSpriteCacheType cacheType) {
+ Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(filename);
+ if (!file) {
+ _gameRef->LOG(0, "BaseSprite::LoadFile failed for file '%s'", filename.c_str());
+ if (_gameRef->_debugDebugMode) {
+ return loadFile("invalid_debug.bmp", lifeTime, cacheType);
+ } else {
+ return loadFile("invalid.bmp", lifeTime, cacheType);
+ }
+ } else {
+ BaseFileManager::getEngineInstance()->closeFile(file);
+ file = NULL;
+ }
+
+ bool ret = STATUS_FAILED;
+
+ AnsiString filePrefix = filename;
+ AnsiString ext = PathUtil::getExtension(filename);
+ ext.toLowercase();
+ filePrefix.toLowercase();
+ if (filePrefix.hasPrefix("savegame:") || (ext == "bmp") || (ext == "tga") || (ext == "png") || (ext == "jpg")) {
+ BaseFrame *frame = new BaseFrame(_gameRef);
+ BaseSubFrame *subframe = new BaseSubFrame(_gameRef);
+ subframe->setSurface(filename, true, 0, 0, 0, lifeTime, true);
+ if (subframe->_surface == NULL) {
+ _gameRef->LOG(0, "Error loading simple sprite '%s'", filename.c_str());
+ ret = STATUS_FAILED;
+ delete frame;
+ delete subframe;
+ } else {
+ subframe->setDefaultRect();
+ frame->_subframes.add(subframe);
+ _frames.add(frame);
+ _currentFrame = 0;
+ ret = STATUS_OK;
+ }
+ } else {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer) {
+ if (DID_FAIL(ret = loadBuffer(buffer, true, lifeTime, cacheType))) {
+ _gameRef->LOG(0, "Error parsing SPRITE file '%s'", filename.c_str());
+ } else {
+ ret = STATUS_OK;
+ }
+ delete[] buffer;
+ }
+ }
+
+ setFilename(filename.c_str());
+
+ return ret;
+}
+
+
+
+TOKEN_DEF_START
+TOKEN_DEF(CONTINUOUS)
+TOKEN_DEF(SPRITE)
+TOKEN_DEF(LOOPING)
+TOKEN_DEF(FRAME)
+TOKEN_DEF(NAME)
+TOKEN_DEF(PRECISE)
+TOKEN_DEF(EDITOR_MUTED)
+TOKEN_DEF(STREAMED_KEEP_LOADED)
+TOKEN_DEF(STREAMED)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(EDITOR_BG_FILE)
+TOKEN_DEF(EDITOR_BG_OFFSET_X)
+TOKEN_DEF(EDITOR_BG_OFFSET_Y)
+TOKEN_DEF(EDITOR_BG_ALPHA)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////
+bool BaseSprite::loadBuffer(byte *buffer, bool complete, int lifeTime, TSpriteCacheType cacheType) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(CONTINUOUS)
+ TOKEN_TABLE(SPRITE)
+ TOKEN_TABLE(LOOPING)
+ TOKEN_TABLE(FRAME)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(PRECISE)
+ TOKEN_TABLE(EDITOR_MUTED)
+ TOKEN_TABLE(STREAMED_KEEP_LOADED)
+ TOKEN_TABLE(STREAMED)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(EDITOR_BG_FILE)
+ TOKEN_TABLE(EDITOR_BG_OFFSET_X)
+ TOKEN_TABLE(EDITOR_BG_OFFSET_Y)
+ TOKEN_TABLE(EDITOR_BG_ALPHA)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+
+ cleanup();
+
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_SPRITE) {
+ _gameRef->LOG(0, "'SPRITE' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ int frameCount = 1;
+ BaseFrame *frame;
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_CONTINUOUS:
+ parser.scanStr((char *)params, "%b", &_continuous);
+ break;
+
+ case TOKEN_EDITOR_MUTED:
+ parser.scanStr((char *)params, "%b", &_editorMuted);
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_LOOPING:
+ parser.scanStr((char *)params, "%b", &_looping);
+ break;
+
+ case TOKEN_PRECISE:
+ parser.scanStr((char *)params, "%b", &_precise);
+ break;
+
+ case TOKEN_STREAMED:
+ parser.scanStr((char *)params, "%b", &_streamed);
+ if (_streamed && lifeTime == -1) {
+ lifeTime = 500;
+ cacheType = CACHE_ALL;
+ }
+ break;
+
+ case TOKEN_STREAMED_KEEP_LOADED:
+ parser.scanStr((char *)params, "%b", &_streamedKeepLoaded);
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_EDITOR_BG_FILE:
+ if (_gameRef->_editorMode) {
+ delete[] _editorBgFile;
+ _editorBgFile = new char[strlen((char *)params) + 1];
+ if (_editorBgFile) {
+ strcpy(_editorBgFile, (char *)params);
+ }
+ }
+ break;
+
+ case TOKEN_EDITOR_BG_OFFSET_X:
+ parser.scanStr((char *)params, "%d", &_editorBgOffsetX);
+ break;
+
+ case TOKEN_EDITOR_BG_OFFSET_Y:
+ parser.scanStr((char *)params, "%d", &_editorBgOffsetY);
+ break;
+
+ case TOKEN_EDITOR_BG_ALPHA:
+ parser.scanStr((char *)params, "%d", &_editorBgAlpha);
+ _editorBgAlpha = MIN(_editorBgAlpha, 255);
+ _editorBgAlpha = MAX(_editorBgAlpha, 0);
+ break;
+
+ case TOKEN_FRAME: {
+ int frameLifeTime = lifeTime;
+ if (cacheType == CACHE_HALF && frameCount % 2 != 1) {
+ frameLifeTime = -1;
+ }
+
+ frame = new BaseFrame(_gameRef);
+
+ if (DID_FAIL(frame->loadBuffer(params, frameLifeTime, _streamedKeepLoaded))) {
+ delete frame;
+ _gameRef->LOG(0, "Error parsing frame %d", frameCount);
+ return STATUS_FAILED;
+ }
+
+ _frames.add(frame);
+ frameCount++;
+ if (_currentFrame == -1) {
+ _currentFrame = 0;
+ }
+ }
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in SPRITE definition");
+ return STATUS_FAILED;
+ }
+ _canBreak = !_continuous;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseSprite::reset() {
+ if (_frames.size() > 0) {
+ _currentFrame = 0;
+ } else {
+ _currentFrame = -1;
+ }
+
+ killAllSounds();
+
+ _lastFrameTime = 0;
+ _finished = false;
+ _moveX = _moveY = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSprite::getCurrentFrame(float zoomX, float zoomY) {
+ //if (_owner && _owner->_freezable && _gameRef->_state == GAME_FROZEN) return true;
+
+ if (_currentFrame == -1) {
+ return false;
+ }
+
+ uint32 timer;
+ if (_owner && _owner->_freezable) {
+ timer = _gameRef->_timer;
+ } else {
+ timer = _gameRef->_liveTimer;
+ }
+
+ int lastFrame = _currentFrame;
+
+ // get current frame
+ if (!_paused && !_finished && timer >= _lastFrameTime + _frames[_currentFrame]->_delay && _lastFrameTime != 0) {
+ if (_currentFrame < (int32)_frames.size() - 1) {
+ _currentFrame++;
+ if (_continuous) {
+ _canBreak = (_currentFrame == (int32)_frames.size() - 1);
+ }
+ } else {
+ if (_looping) {
+ _currentFrame = 0;
+ _canBreak = true;
+ } else {
+ _finished = true;
+ _canBreak = true;
+ }
+ }
+
+ _lastFrameTime = timer;
+ }
+
+ _changed = (lastFrame != _currentFrame || (_looping && (int32)_frames.size() == 1));
+
+ if (_lastFrameTime == 0) {
+ _lastFrameTime = timer;
+ _changed = true;
+ if (_continuous) {
+ _canBreak = (_currentFrame == (int32)_frames.size() - 1);
+ }
+ }
+
+ if (_changed) {
+ _moveX = _frames[_currentFrame]->_moveX;
+ _moveY = _frames[_currentFrame]->_moveY;
+
+ if (zoomX != 100 || zoomY != 100) {
+ _moveX = (int)((float)_moveX * (float)(zoomX / 100.0f));
+ _moveY = (int)((float)_moveY * (float)(zoomY / 100.0f));
+ }
+ }
+
+ return _changed;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSprite::display(int x, int y, BaseObject *registerVal, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode) {
+ if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) {
+ return STATUS_OK;
+ }
+
+ // on change...
+ if (_changed) {
+ if (_frames[_currentFrame]->_killSound) {
+ killAllSounds();
+ }
+ applyEvent("FrameChanged");
+ _frames[_currentFrame]->oneTimeDisplay(_owner, _gameRef->_editorMode && _editorMuted);
+ }
+
+ // draw frame
+ return _frames[_currentFrame]->draw(x - _gameRef->_offsetX, y - _gameRef->_offsetY, registerVal, zoomX, zoomY, _precise, alpha, _editorAllFrames, rotate, blendMode);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSurface *BaseSprite::getSurface() {
+ // only used for animated textures for 3D models
+ if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) {
+ return NULL;
+ }
+ BaseFrame *frame = _frames[_currentFrame];
+ if (frame && frame->_subframes.size() > 0) {
+ BaseSubFrame *subframe = frame->_subframes[0];
+ if (subframe) {
+ return subframe->_surface;
+ } else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSprite::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) {
+ if (!rect) {
+ return false;
+ }
+
+ BasePlatform::setRectEmpty(rect);
+ for (uint32 i = 0; i < _frames.size(); i++) {
+ Rect32 frame;
+ Rect32 temp;
+ BasePlatform::copyRect(&temp, rect);
+ _frames[i]->getBoundingRect(&frame, x, y, scaleX, scaleY);
+ BasePlatform::unionRect(rect, &temp, &frame);
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSprite::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "SPRITE {\n");
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "LOOPING=%s\n", _looping ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "CONTINUOUS=%s\n", _continuous ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "PRECISE=%s\n", _precise ? "TRUE" : "FALSE");
+ if (_streamed) {
+ buffer->putTextIndent(indent + 2, "STREAMED=%s\n", _streamed ? "TRUE" : "FALSE");
+
+ if (_streamedKeepLoaded) {
+ buffer->putTextIndent(indent + 2, "STREAMED_KEEP_LOADED=%s\n", _streamedKeepLoaded ? "TRUE" : "FALSE");
+ }
+ }
+
+ if (_editorMuted) {
+ buffer->putTextIndent(indent + 2, "EDITOR_MUTED=%s\n", _editorMuted ? "TRUE" : "FALSE");
+ }
+
+ if (_editorBgFile) {
+ buffer->putTextIndent(indent + 2, "EDITOR_BG_FILE=\"%s\"\n", _editorBgFile);
+ buffer->putTextIndent(indent + 2, "EDITOR_BG_OFFSET_X=%d\n", _editorBgOffsetX);
+ buffer->putTextIndent(indent + 2, "EDITOR_BG_OFFSET_Y=%d\n", _editorBgOffsetY);
+ buffer->putTextIndent(indent + 2, "EDITOR_BG_ALPHA=%d\n", _editorBgAlpha);
+ }
+
+ BaseScriptHolder::saveAsText(buffer, indent + 2);
+
+ // scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ for (uint32 i = 0; i < _frames.size(); i++) {
+ _frames[i]->saveAsText(buffer, indent + 2);
+ }
+
+ buffer->putTextIndent(indent, "}\n\n");
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSprite::persist(BasePersistenceManager *persistMgr) {
+ BaseScriptHolder::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_canBreak));
+ persistMgr->transfer(TMEMBER(_changed));
+ persistMgr->transfer(TMEMBER(_paused));
+ persistMgr->transfer(TMEMBER(_continuous));
+ persistMgr->transfer(TMEMBER(_currentFrame));
+ persistMgr->transfer(TMEMBER(_editorAllFrames));
+ persistMgr->transfer(TMEMBER(_editorBgAlpha));
+ persistMgr->transfer(TMEMBER(_editorBgFile));
+ persistMgr->transfer(TMEMBER(_editorBgOffsetX));
+ persistMgr->transfer(TMEMBER(_editorBgOffsetY));
+ persistMgr->transfer(TMEMBER(_editorMuted));
+ persistMgr->transfer(TMEMBER(_finished));
+
+ _frames.persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_lastFrameTime));
+ persistMgr->transfer(TMEMBER(_looping));
+ persistMgr->transfer(TMEMBER(_moveX));
+ persistMgr->transfer(TMEMBER(_moveY));
+ persistMgr->transfer(TMEMBER(_owner));
+ persistMgr->transfer(TMEMBER(_precise));
+ persistMgr->transfer(TMEMBER(_streamed));
+ persistMgr->transfer(TMEMBER(_streamedKeepLoaded));
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseSprite::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // GetFrame
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetFrame") == 0) {
+ stack->correctParams(1);
+ int index = stack->pop()->getInt(-1);
+ if (index < 0 || index >= (int32)_frames.size()) {
+ script->runtimeError("Sprite.GetFrame: Frame index %d is out of range.", index);
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_frames[index], true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteFrame
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteFrame") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ if (val->isInt()) {
+ int index = val->getInt(-1);
+ if (index < 0 || index >= (int32)_frames.size()) {
+ script->runtimeError("Sprite.DeleteFrame: Frame index %d is out of range.", index);
+ }
+ } else {
+ BaseFrame *frame = (BaseFrame *)val->getNative();
+ for (uint32 i = 0; i < _frames.size(); i++) {
+ if (_frames[i] == frame) {
+ if (i == (uint32)_currentFrame) {
+ _lastFrameTime = 0;
+ }
+ delete _frames[i];
+ _frames.remove_at(i);
+ break;
+ }
+ }
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Reset
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Reset") == 0) {
+ stack->correctParams(0);
+ reset();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddFrame
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddFrame") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ const char *filename = NULL;
+ if (!val->isNULL()) {
+ filename = val->getString();
+ }
+
+ BaseFrame *frame = new BaseFrame(_gameRef);
+ if (filename != NULL) {
+ BaseSubFrame *sub = new BaseSubFrame(_gameRef);
+ if (DID_SUCCEED(sub->setSurface(filename))) {
+ sub->setDefaultRect();
+ frame->_subframes.add(sub);
+ } else {
+ delete sub;
+ }
+ }
+ _frames.add(frame);
+
+ stack->pushNative(frame, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InsertFrame
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InsertFrame") == 0) {
+ stack->correctParams(2);
+ int index = stack->pop()->getInt();
+ if (index < 0) {
+ index = 0;
+ }
+
+ ScValue *val = stack->pop();
+ const char *filename = NULL;
+ if (!val->isNULL()) {
+ filename = val->getString();
+ }
+
+ BaseFrame *frame = new BaseFrame(_gameRef);
+ if (filename != NULL) {
+ BaseSubFrame *sub = new BaseSubFrame(_gameRef);
+ if (DID_SUCCEED(sub->setSurface(filename))) {
+ frame->_subframes.add(sub);
+ } else {
+ delete sub;
+ }
+ }
+
+ if (index >= (int32)_frames.size()) {
+ _frames.add(frame);
+ } else {
+ _frames.insert_at(index, frame);
+ }
+
+ stack->pushNative(frame, true);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Pause
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Pause") == 0) {
+ stack->correctParams(0);
+ _paused = true;
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Play
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Play") == 0) {
+ stack->correctParams(0);
+ _paused = false;
+ stack->pushNULL();
+ return STATUS_OK;
+ } else {
+ return BaseScriptHolder::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseSprite::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("sprite");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumFrames (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumFrames") {
+ _scValue->setInt(_frames.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CurrentFrame
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "CurrentFrame") {
+ _scValue->setInt(_currentFrame);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PixelPerfect
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "PixelPerfect") {
+ _scValue->setBool(_precise);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Looping
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Looping") {
+ _scValue->setBool(_looping);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Owner (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Owner") {
+ if (_owner == NULL) {
+ _scValue->setNULL();
+ } else {
+ _scValue->setNative(_owner, true);
+ }
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Finished (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Finished") {
+ _scValue->setBool(_finished);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Paused (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Paused") {
+ _scValue->setBool(_paused);
+ return _scValue;
+ } else {
+ return BaseScriptHolder::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSprite::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // CurrentFrame
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "CurrentFrame") == 0) {
+ _currentFrame = value->getInt(0);
+ if (_currentFrame >= (int32)_frames.size() || _currentFrame < 0) {
+ _currentFrame = -1;
+ }
+ _lastFrameTime = 0;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PixelPerfect
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PixelPerfect") == 0) {
+ _precise = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Looping
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Looping") == 0) {
+ _looping = value->getBool();
+ return STATUS_OK;
+ } else {
+ return BaseScriptHolder::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseSprite::scToString() {
+ return "[sprite]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSprite::killAllSounds() {
+ for (uint32 i = 0; i < _frames.size(); i++) {
+ _frames[i]->stopSound();
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_sprite.h b/engines/wintermute/base/base_sprite.h
new file mode 100644
index 0000000000..1d244c3a52
--- /dev/null
+++ b/engines/wintermute/base/base_sprite.h
@@ -0,0 +1,93 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SPRITE_H
+#define WINTERMUTE_BASE_SPRITE_H
+
+
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/base/base_script_holder.h"
+
+namespace Wintermute {
+class BaseFrame;
+class BaseSurface;
+class BaseObject;
+class BaseSprite: public BaseScriptHolder {
+public:
+ BaseSurface *getSurface();
+ void cleanup();
+ void setDefaults();
+ DECLARE_PERSISTENT(BaseSprite, BaseScriptHolder)
+
+ bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100);
+ int _moveY;
+ int _moveX;
+ bool display(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, uint32 alpha = 0xFFFFFFFF, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL);
+ bool getCurrentFrame(float zoomX = 100, float zoomY = 100);
+ void reset();
+ bool isChanged();
+ bool isFinished();
+ bool loadBuffer(byte *buffer, bool compete = true, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL);
+ bool loadFile(const Common::String &filename, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL);
+ bool draw(int x, int y, BaseObject *Register = NULL, float zoomX = 100, float zoomY = 100, uint32 alpha = 0xFFFFFFFF);
+ bool _looping;
+ int _currentFrame;
+ bool addFrame(const char *filename, uint32 delay = 0, int hotspotX = 0, int hotspotY = 0, Rect32 *rect = NULL);
+ BaseSprite(BaseGame *inGame, BaseObject *owner = NULL);
+ virtual ~BaseSprite();
+ BaseArray<BaseFrame *> _frames;
+ bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+private:
+ BaseObject *_owner;
+ bool _canBreak;
+ bool _changed;
+ bool _editorAllFrames;
+ char *_editorBgFile;
+ int _editorBgOffsetX;
+ int _editorBgOffsetY;
+ int _editorBgAlpha;
+ bool _editorMuted;
+ bool _finished;
+ bool _continuous;
+ uint32 _lastFrameTime;
+ bool _precise;
+ bool _paused;
+ bool _streamed;
+ bool _streamedKeepLoaded;
+ bool killAllSounds();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_string_table.cpp b/engines/wintermute/base/base_string_table.cpp
new file mode 100644
index 0000000000..2f890beea1
--- /dev/null
+++ b/engines/wintermute/base/base_string_table.cpp
@@ -0,0 +1,255 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+BaseStringTable::BaseStringTable(BaseGame *inGame) : BaseClass(inGame) {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseStringTable::~BaseStringTable() {
+ // delete strings
+ _strings.clear();
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseStringTable::addString(const char *key, const char *val, bool reportDuplicities) {
+ if (key == NULL || val == NULL) {
+ return STATUS_FAILED;
+ }
+
+ if (scumm_stricmp(key, "@right-to-left") == 0) {
+ _gameRef->_textRTL = true;
+ return STATUS_OK;
+ }
+
+ Common::String finalKey = key;
+ finalKey.toLowercase();
+
+ StringsIter it = _strings.find(finalKey);
+ if (it != _strings.end() && reportDuplicities) {
+ _gameRef->LOG(0, " Warning: Duplicate definition of string '%s'.", finalKey.c_str());
+ }
+
+ _strings[finalKey] = val;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+char *BaseStringTable::getKey(const char *str) const {
+ if (str == NULL || str[0] != '/') {
+ return NULL;
+ }
+
+ const char *value = strchr(str + 1, '/');
+ if (value == NULL) {
+ return NULL;
+ }
+
+ char *key = new char[value - str];
+ Common::strlcpy(key, str + 1, (size_t)(value - str));
+
+ BasePlatform::strlwr(key);
+
+ char *newStr;
+
+ StringsIter it = _strings.find(key);
+ if (it != _strings.end()) {
+ newStr = new char[it->_value.size() + 1];
+ strcpy(newStr, it->_value.c_str());
+ if (strlen(newStr) > 0 && newStr[0] == '/' && strchr(newStr + 1, '/')) {
+ delete[] key;
+ char *ret = getKey(newStr);
+ delete[] newStr;
+ return ret;
+ } else {
+ delete[] newStr;
+ return key;
+ }
+ } else {
+ return key;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseStringTable::expand(char **str) const {
+ if (str == NULL || *str == NULL || *str[0] != '/') {
+ return;
+ }
+
+ char *value = strchr(*str + 1, '/');
+ if (value == NULL) {
+ return;
+ }
+
+ char *key = new char[value - *str];
+ Common::strlcpy(key, *str + 1, (size_t)(value - *str));
+
+ BasePlatform::strlwr(key);
+
+ value++;
+
+ char *newStr;
+
+ StringsIter it = _strings.find(key);
+ if (it != _strings.end()) {
+ newStr = new char[it->_value.size() + 1];
+ strcpy(newStr, it->_value.c_str());
+ } else {
+ newStr = new char[strlen(value) + 1];
+ strcpy(newStr, value);
+ }
+
+ delete[] key;
+ delete[] *str;
+ *str = newStr;
+
+ if (strlen(*str) > 0 && *str[0] == '/') {
+ expand(str);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseStringTable::expandStatic(const char *string) const {
+ if (string == NULL || string[0] == '\0' || string[0] != '/') {
+ return string;
+ }
+
+ const char *value = strchr(string + 1, '/');
+ if (value == NULL) {
+ return string;
+ }
+
+ char *key = new char[value - string];
+ Common::strlcpy(key, string + 1, (size_t)(value - string - 1));
+ BasePlatform::strlwr(key);
+
+ value++;
+
+ const char *newStr;
+
+ StringsIter it = _strings.find(key);
+ if (it != _strings.end()) {
+ newStr = it->_value.c_str();
+ } else {
+ newStr = value;
+ }
+
+ delete[] key;
+
+ if (strlen(newStr) > 0 && newStr[0] == '/') {
+ return expandStatic(newStr);
+ } else {
+ return newStr;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseStringTable::loadFile(const char *filename, bool clearOld) {
+ _gameRef->LOG(0, "Loading string table...");
+
+ if (clearOld) {
+ _strings.clear();
+ }
+
+ uint32 size;
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &size);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "BaseStringTable::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ uint32 pos = 0;
+
+ if (size > 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) {
+ pos += 3;
+ if (_gameRef->_textEncoding != TEXT_UTF8) {
+ _gameRef->_textEncoding = TEXT_UTF8;
+ //_gameRef->_textEncoding = TEXT_ANSI;
+ _gameRef->LOG(0, " UTF8 file detected, switching to UTF8 text encoding");
+ }
+ } else {
+ _gameRef->_textEncoding = TEXT_ANSI;
+ }
+
+ uint32 lineLength = 0;
+ while (pos < size) {
+ lineLength = 0;
+ while (pos + lineLength < size && buffer[pos + lineLength] != '\n' && buffer[pos + lineLength] != '\0') {
+ lineLength++;
+ }
+
+ uint32 realLength = lineLength - (pos + lineLength >= size ? 0 : 1);
+ char *line = new char[realLength + 1];
+ Common::strlcpy(line, (char *)&buffer[pos], realLength + 1);
+ char *value = strchr(line, '\t');
+ if (value == NULL) {
+ value = strchr(line, ' ');
+ }
+
+ if (line[0] != ';') {
+ if (value != NULL) {
+ value[0] = '\0';
+ value++;
+ for (uint32 i = 0; i < strlen(value); i++) {
+ if (value[i] == '|') {
+ value[i] = '\n';
+ }
+ }
+ addString(line, value, clearOld);
+ } else if (line[0] != '\0') {
+ addString(line, "", clearOld);
+ }
+ }
+
+ delete[] line;
+ pos += lineLength + 1;
+ }
+
+ delete[] buffer;
+
+ _gameRef->LOG(0, " %d strings loaded", _strings.size());
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_string_table.h b/engines/wintermute/base/base_string_table.h
new file mode 100644
index 0000000000..128807bd1a
--- /dev/null
+++ b/engines/wintermute/base/base_string_table.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_STRING_TABLE_H
+#define WINTERMUTE_BASE_STRING_TABLE_H
+
+
+#include "common/hashmap.h"
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+class BaseStringTable : public BaseClass {
+public:
+ bool loadFile(const char *filename, bool deleteAll = true);
+ void expand(char **str) const;
+ const char *expandStatic(const char *string) const;
+ bool addString(const char *key, const char *val, bool reportDuplicities = true);
+ BaseStringTable(BaseGame *inGame);
+ virtual ~BaseStringTable();
+ char *getKey(const char *str) const;
+private:
+ Common::HashMap<Common::String, Common::String> _strings;
+ typedef Common::HashMap<Common::String, Common::String>::const_iterator StringsIter;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_sub_frame.cpp b/engines/wintermute/base/base_sub_frame.cpp
new file mode 100644
index 0000000000..77cc522ae7
--- /dev/null
+++ b/engines/wintermute/base/base_sub_frame.cpp
@@ -0,0 +1,659 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/base_active_rect.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "engines/wintermute/base/base_surface_storage.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseSubFrame, false)
+
+//////////////////////////////////////////////////////////////////////////
+BaseSubFrame::BaseSubFrame(BaseGame *inGame) : BaseScriptable(inGame, true) {
+ _surface = NULL;
+ _hotspotX = _hotspotY = 0;
+ _alpha = 0xFFFFFFFF;
+ _transparent = 0xFFFF00FF;
+
+ _wantsDefaultRect = false;
+ BasePlatform::setRectEmpty(&_rect);
+
+ _editorSelected = false;
+
+ _surfaceFilename = NULL;
+ _cKDefault = true;
+ _cKRed = _cKBlue = _cKGreen = 0;
+ _lifeTime = -1;
+ _keepLoaded = false;
+
+ _2DOnly = _3DOnly = false;
+ _decoration = false;
+
+ _mirrorX = _mirrorY = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSubFrame::~BaseSubFrame() {
+ if (_surface) {
+ _gameRef->_surfaceStorage->removeSurface(_surface);
+ }
+ delete[] _surfaceFilename;
+ _surfaceFilename = NULL;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(TRANSPARENT)
+TOKEN_DEF(RECT)
+TOKEN_DEF(HOTSPOT)
+TOKEN_DEF(2D_ONLY)
+TOKEN_DEF(3D_ONLY)
+TOKEN_DEF(DECORATION)
+TOKEN_DEF(ALPHA_COLOR)
+TOKEN_DEF(ALPHA)
+TOKEN_DEF(MIRROR_X)
+TOKEN_DEF(MIRROR_Y)
+TOKEN_DEF(EDITOR_SELECTED)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::loadBuffer(byte *buffer, int lifeTime, bool keepLoaded) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(TRANSPARENT)
+ TOKEN_TABLE(RECT)
+ TOKEN_TABLE(HOTSPOT)
+ TOKEN_TABLE(2D_ONLY)
+ TOKEN_TABLE(3D_ONLY)
+ TOKEN_TABLE(DECORATION)
+ TOKEN_TABLE(ALPHA_COLOR)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE(MIRROR_X)
+ TOKEN_TABLE(MIRROR_Y)
+ TOKEN_TABLE(EDITOR_SELECTED)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ char *params;
+ int cmd;
+ BaseParser parser;
+ Rect32 rect;
+ int r = 255, g = 255, b = 255;
+ int ar = 255, ag = 255, ab = 255, alpha = 255;
+ bool custoTrans = false;
+ BasePlatform::setRectEmpty(&rect);
+ char *surfaceFile = NULL;
+
+ delete _surface;
+ _surface = NULL;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, &params)) > 0) {
+ switch (cmd) {
+ case TOKEN_IMAGE:
+ surfaceFile = params;
+ break;
+
+ case TOKEN_TRANSPARENT:
+ parser.scanStr(params, "%d,%d,%d", &r, &g, &b);
+ custoTrans = true;
+ break;
+
+ case TOKEN_RECT:
+ parser.scanStr(params, "%d,%d,%d,%d", &rect.left, &rect.top, &rect.right, &rect.bottom);
+ break;
+
+ case TOKEN_HOTSPOT:
+ parser.scanStr(params, "%d,%d", &_hotspotX, &_hotspotY);
+ break;
+
+ case TOKEN_2D_ONLY:
+ parser.scanStr(params, "%b", &_2DOnly);
+ break;
+
+ case TOKEN_3D_ONLY:
+ parser.scanStr(params, "%b", &_3DOnly);
+ break;
+
+ case TOKEN_MIRROR_X:
+ parser.scanStr(params, "%b", &_mirrorX);
+ break;
+
+ case TOKEN_MIRROR_Y:
+ parser.scanStr(params, "%b", &_mirrorY);
+ break;
+
+ case TOKEN_DECORATION:
+ parser.scanStr(params, "%b", &_decoration);
+ break;
+
+ case TOKEN_ALPHA_COLOR:
+ parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab);
+ break;
+
+ case TOKEN_ALPHA:
+ parser.scanStr(params, "%d", &alpha);
+ break;
+
+ case TOKEN_EDITOR_SELECTED:
+ parser.scanStr(params, "%b", &_editorSelected);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty((byte *)params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in SUBFRAME definition");
+ return STATUS_FAILED;
+ }
+
+ if (surfaceFile != NULL) {
+ if (custoTrans) {
+ setSurface(surfaceFile, false, r, g, b, lifeTime, keepLoaded);
+ } else {
+ setSurface(surfaceFile, true, 0, 0, 0, lifeTime, keepLoaded);
+ }
+ }
+
+ _alpha = BYTETORGBA(ar, ag, ab, alpha);
+ if (custoTrans) {
+ _transparent = BYTETORGBA(r, g, b, 0xFF);
+ }
+
+ /*
+ if (_surface == NULL)
+ {
+ _gameRef->LOG(0, "Error parsing sub-frame. Image not set.");
+ return STATUS_FAILED;
+ }
+ */
+ if (BasePlatform::isRectEmpty(&rect)) {
+ setDefaultRect();
+ } else {
+ setRect(rect);
+ }
+
+ return STATUS_OK;
+}
+
+Rect32 BaseSubFrame::getRect() {
+ if (_wantsDefaultRect && _surface) {
+ BasePlatform::setRect(&_rect, 0, 0, _surface->getWidth(), _surface->getHeight());
+ _wantsDefaultRect = false;
+ }
+ return _rect;
+}
+
+void BaseSubFrame::setRect(Rect32 rect) {
+ _wantsDefaultRect = false;
+ _rect = rect;
+}
+
+const char* BaseSubFrame::getSurfaceFilename() {
+ return _surfaceFilename;
+}
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, bool precise, uint32 alpha, float rotate, TSpriteBlendMode blendMode) {
+ if (!_surface) {
+ return STATUS_OK;
+ }
+
+ if (registerOwner != NULL && !_decoration) {
+ if (zoomX == 100 && zoomY == 100) {
+ _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, registerOwner, this, x - _hotspotX + getRect().left, y - _hotspotY + getRect().top, getRect().right - getRect().left, getRect().bottom - getRect().top, zoomX, zoomY, precise));
+ } else {
+ _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, registerOwner, this, (int)(x - (_hotspotX + getRect().left) * (zoomX / 100)), (int)(y - (_hotspotY + getRect().top) * (zoomY / 100)), (int)((getRect().right - getRect().left) * (zoomX / 100)), (int)((getRect().bottom - getRect().top) * (zoomY / 100)), zoomX, zoomY, precise));
+ }
+ }
+ if (_gameRef->_suspendedRendering) {
+ return STATUS_OK;
+ }
+
+ bool res;
+
+ //if (Alpha==0xFFFFFFFF) Alpha = _alpha; // TODO: better (combine owner's and self alpha)
+ if (_alpha != 0xFFFFFFFF) {
+ alpha = _alpha;
+ }
+
+ if (rotate != 0.0f) {
+ res = _surface->displayTransform((int)(x - _hotspotX * (zoomX / 100)), (int)(y - _hotspotY * (zoomY / 100)), _hotspotX, _hotspotY, getRect(), zoomX, zoomY, alpha, rotate, blendMode, _mirrorX, _mirrorY);
+ } else {
+ if (zoomX == 100 && zoomY == 100) {
+ res = _surface->displayTrans(x - _hotspotX, y - _hotspotY, getRect(), alpha, blendMode, _mirrorX, _mirrorY);
+ } else {
+ res = _surface->displayTransZoom((int)(x - _hotspotX * (zoomX / 100)), (int)(y - _hotspotY * (zoomY / 100)), getRect(), zoomX, zoomY, alpha, blendMode, _mirrorX, _mirrorY);
+ }
+ }
+
+ return res;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) {
+ if (!rect) {
+ return false;
+ }
+
+ float ratioX = scaleX / 100.0f;
+ float ratioY = scaleY / 100.0f;
+
+ BasePlatform::setRect(rect,
+ (int)(x - _hotspotX * ratioX),
+ (int)(y - _hotspotY * ratioY),
+ (int)(x - _hotspotX * ratioX + (getRect().right - getRect().left) * ratioX),
+ (int)(y - _hotspotY * ratioY + (getRect().bottom - getRect().top) * ratioY));
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::saveAsText(BaseDynamicBuffer *buffer, int indent, bool complete) {
+ if (complete) {
+ buffer->putTextIndent(indent, "SUBFRAME {\n");
+ }
+
+ if (_surface && _surface->getFileNameStr() != "") {
+ buffer->putTextIndent(indent + 2, "IMAGE = \"%s\"\n", _surface->getFileName());
+ }
+
+ if (_transparent != 0xFFFF00FF) {
+ buffer->putTextIndent(indent + 2, "TRANSPARENT { %d,%d,%d }\n", RGBCOLGetR(_transparent), RGBCOLGetG(_transparent), RGBCOLGetB(_transparent));
+ }
+
+ Rect32 rect;
+ BasePlatform::setRectEmpty(&rect);
+ if (_surface) {
+ BasePlatform::setRect(&rect, 0, 0, _surface->getWidth(), _surface->getHeight());
+ }
+ if (!(rect == getRect())) {
+ buffer->putTextIndent(indent + 2, "RECT { %d,%d,%d,%d }\n", getRect().left, getRect().top, getRect().right, getRect().bottom);
+ }
+
+ if (_hotspotX != 0 || _hotspotY != 0) {
+ buffer->putTextIndent(indent + 2, "HOTSPOT {%d, %d}\n", _hotspotX, _hotspotY);
+ }
+
+ if (_alpha != 0xFFFFFFFF) {
+ buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alpha), RGBCOLGetG(_alpha), RGBCOLGetB(_alpha));
+ buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alpha));
+ }
+
+ if (_mirrorX) {
+ buffer->putTextIndent(indent + 2, "MIRROR_X=%s\n", _mirrorX ? "TRUE" : "FALSE");
+ }
+
+ if (_mirrorY) {
+ buffer->putTextIndent(indent + 2, "MIRROR_Y=%s\n", _mirrorY ? "TRUE" : "FALSE");
+ }
+
+ if (_2DOnly) {
+ buffer->putTextIndent(indent + 2, "2D_ONLY=%s\n", _2DOnly ? "TRUE" : "FALSE");
+ }
+
+ if (_3DOnly) {
+ buffer->putTextIndent(indent + 2, "3D_ONLY=%s\n", _3DOnly ? "TRUE" : "FALSE");
+ }
+
+ if (_decoration) {
+ buffer->putTextIndent(indent + 2, "DECORATION=%s\n", _decoration ? "TRUE" : "FALSE");
+ }
+
+ if (_editorSelected) {
+ buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE");
+ }
+
+ BaseClass::saveAsText(buffer, indent + 2);
+
+
+ if (complete) {
+ buffer->putTextIndent(indent, "}\n\n");
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSubFrame::setDefaultRect() {
+ if (_surface) {
+ _wantsDefaultRect = true;
+ } else {
+ _wantsDefaultRect = false;
+ BasePlatform::setRectEmpty(&_rect);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::persist(BasePersistenceManager *persistMgr) {
+
+ BaseScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_2DOnly));
+ persistMgr->transfer(TMEMBER(_3DOnly));
+ persistMgr->transfer(TMEMBER(_alpha));
+ persistMgr->transfer(TMEMBER(_decoration));
+ persistMgr->transfer(TMEMBER(_editorSelected));
+ persistMgr->transfer(TMEMBER(_hotspotX));
+ persistMgr->transfer(TMEMBER(_hotspotY));
+ persistMgr->transfer(TMEMBER(_rect));
+ persistMgr->transfer(TMEMBER(_wantsDefaultRect));
+
+ persistMgr->transfer(TMEMBER(_surfaceFilename));
+ persistMgr->transfer(TMEMBER(_cKDefault));
+ persistMgr->transfer(TMEMBER(_cKRed));
+ persistMgr->transfer(TMEMBER(_cKGreen));
+ persistMgr->transfer(TMEMBER(_cKBlue));
+ persistMgr->transfer(TMEMBER(_lifeTime));
+
+ persistMgr->transfer(TMEMBER(_keepLoaded));
+ persistMgr->transfer(TMEMBER(_mirrorX));
+ persistMgr->transfer(TMEMBER(_mirrorY));
+ persistMgr->transfer(TMEMBER(_transparent));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetImage
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetImage") == 0) {
+ stack->correctParams(0);
+
+ if (!_surfaceFilename) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_surfaceFilename);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetImage") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ if (val->isNULL()) {
+ if (_surface) {
+ _gameRef->_surfaceStorage->removeSurface(_surface);
+ }
+ delete[] _surfaceFilename;
+ _surfaceFilename = NULL;
+ stack->pushBool(true);
+ } else {
+ const char *filename = val->getString();
+ if (DID_SUCCEED(setSurface(filename))) {
+ setDefaultRect();
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+ }
+
+ return STATUS_OK;
+ } else {
+ return BaseScriptable::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *BaseSubFrame::scGetProperty(const Common::String &name) {
+ if (!_scValue) {
+ _scValue = new ScValue(_gameRef);
+ }
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("subframe");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AlphaColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AlphaColor") {
+
+ _scValue->setInt((int)_alpha);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TransparentColor (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TransparentColor") {
+ _scValue->setInt((int)_transparent);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Is2DOnly
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Is2DOnly") {
+ _scValue->setBool(_2DOnly);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Is3DOnly
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Is3DOnly") {
+ _scValue->setBool(_3DOnly);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MirrorX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MirrorX") {
+ _scValue->setBool(_mirrorX);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MirrorY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MirrorY") {
+ _scValue->setBool(_mirrorY);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Decoration
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Decoration") {
+ _scValue->setBool(_decoration);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HotspotX
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "HotspotX") {
+ _scValue->setInt(_hotspotX);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HotspotY
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "HotspotY") {
+ _scValue->setInt(_hotspotY);
+ return _scValue;
+ } else {
+ return BaseScriptable::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // AlphaColor
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "AlphaColor") == 0) {
+ _alpha = (uint32)value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Is2DOnly
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Is2DOnly") == 0) {
+ _2DOnly = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Is3DOnly
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Is3DOnly") == 0) {
+ _3DOnly = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MirrorX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MirrorX") == 0) {
+ _mirrorX = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MirrorY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MirrorY") == 0) {
+ _mirrorY = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Decoration
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Decoration") == 0) {
+ _decoration = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HotspotX
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HotspotX") == 0) {
+ _hotspotX = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HotspotY
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HotspotY") == 0) {
+ _hotspotY = value->getInt();
+ return STATUS_OK;
+ } else {
+ return BaseScriptable::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *BaseSubFrame::scToString() {
+ return "[subframe]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::setSurface(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) {
+ if (_surface) {
+ _gameRef->_surfaceStorage->removeSurface(_surface);
+ _surface = NULL;
+ }
+
+ delete[] _surfaceFilename;
+ _surfaceFilename = NULL;
+
+ _surface = _gameRef->_surfaceStorage->addSurface(filename, defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded);
+ if (_surface) {
+ _surfaceFilename = new char[filename.size() + 1];
+ strcpy(_surfaceFilename, filename.c_str());
+
+ _cKDefault = defaultCK;
+ _cKRed = ckRed;
+ _cKGreen = ckGreen;
+ _cKBlue = ckBlue;
+ _lifeTime = lifeTime;
+ _keepLoaded = keepLoaded;
+
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSubFrame::setSurfaceSimple() {
+ if (!_surfaceFilename) {
+ _surface = NULL;
+ return STATUS_OK;
+ }
+ _surface = _gameRef->_surfaceStorage->addSurface(_surfaceFilename, _cKDefault, _cKRed, _cKGreen, _cKBlue, _lifeTime, _keepLoaded);
+ if (_surface) {
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_sub_frame.h b/engines/wintermute/base/base_sub_frame.h
new file mode 100644
index 0000000000..c173ae69d1
--- /dev/null
+++ b/engines/wintermute/base/base_sub_frame.h
@@ -0,0 +1,93 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SUBFRAME_H
+#define WINTERMUTE_BASE_SUBFRAME_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/base/base_scriptable.h"
+
+namespace Wintermute {
+class BaseObject;
+class BaseSurface;
+class BaseSubFrame : public BaseScriptable {
+public:
+ bool _mirrorX;
+ bool _mirrorY;
+ bool _decoration;
+ bool setSurface(const Common::String &filename, bool defaultCK = true, byte ckRed = 0, byte ckGreen = 0, byte ckBlue = 0, int lifeTime = -1, bool keepLoaded = false);
+ bool setSurfaceSimple();
+ DECLARE_PERSISTENT(BaseSubFrame, BaseScriptable)
+ void setDefaultRect();
+ uint32 _transparent;
+ bool saveAsText(BaseDynamicBuffer *buffer, int indent) { return saveAsText(buffer, indent, true); }
+ bool saveAsText(BaseDynamicBuffer *buffer, int indent, bool complete);
+ bool _editorSelected;
+ BaseSubFrame(BaseGame *inGame);
+ virtual ~BaseSubFrame();
+ bool loadBuffer(byte *buffer, int lifeTime, bool keepLoaded);
+ bool draw(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, bool precise = true, uint32 alpha = 0xFFFFFFFF, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL);
+ bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100);
+ const char* getSurfaceFilename();
+
+ int _hotspotX;
+ int _hotspotY;
+ uint32 _alpha;
+ // These two setters and getters are rather usefull, as they allow _rect to be lazily defined
+ // Thus we don't need to load the actual graphics before the rect is actually needed.
+ Rect32 getRect();
+ void setRect(Rect32 rect);
+private:
+ bool _wantsDefaultRect;
+ Rect32 _rect;
+ char *_surfaceFilename;
+public:
+ bool _cKDefault;
+ byte _cKRed;
+ byte _cKGreen;
+ byte _cKBlue;
+ int _lifeTime;
+ bool _keepLoaded;
+
+ bool _2DOnly;
+ bool _3DOnly;
+
+ BaseSurface *_surface;
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_surface_storage.cpp b/engines/wintermute/base/base_surface_storage.cpp
new file mode 100644
index 0000000000..4e795ca813
--- /dev/null
+++ b/engines/wintermute/base/base_surface_storage.cpp
@@ -0,0 +1,208 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_surface_storage.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+//IMPLEMENT_PERSISTENT(BaseSurfaceStorage, true);
+
+//////////////////////////////////////////////////////////////////////
+BaseSurfaceStorage::BaseSurfaceStorage(BaseGame *inGame) : BaseClass(inGame) {
+ _lastCleanupTime = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseSurfaceStorage::~BaseSurfaceStorage() {
+ cleanup(true);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceStorage::cleanup(bool warn) {
+ for (uint32 i = 0; i < _surfaces.size(); i++) {
+ if (warn) {
+ _gameRef->LOG(0, "BaseSurfaceStorage warning: purging surface '%s', usage:%d", _surfaces[i]->getFileName(), _surfaces[i]->_referenceCount);
+ }
+ delete _surfaces[i];
+ }
+ _surfaces.clear();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceStorage::initLoop() {
+ if (_gameRef->_smartCache && _gameRef->_liveTimer - _lastCleanupTime >= _gameRef->_surfaceGCCycleTime) {
+ _lastCleanupTime = _gameRef->_liveTimer;
+ sortSurfaces();
+ for (uint32 i = 0; i < _surfaces.size(); i++) {
+ if (_surfaces[i]->_lifeTime <= 0) {
+ break;
+ }
+
+ if (_surfaces[i]->_lifeTime > 0 && _surfaces[i]->_valid && (int)(_gameRef->_liveTimer - _surfaces[i]->_lastUsedTime) >= _surfaces[i]->_lifeTime) {
+ //_gameRef->QuickMessageForm("Invalidating: %s", _surfaces[i]->_filename);
+ _surfaces[i]->invalidate();
+ }
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSurfaceStorage::removeSurface(BaseSurface *surface) {
+ for (uint32 i = 0; i < _surfaces.size(); i++) {
+ if (_surfaces[i] == surface) {
+ _surfaces[i]->_referenceCount--;
+ if (_surfaces[i]->_referenceCount <= 0) {
+ delete _surfaces[i];
+ _surfaces.remove_at(i);
+ }
+ break;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseSurface *BaseSurfaceStorage::addSurface(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) {
+ for (uint32 i = 0; i < _surfaces.size(); i++) {
+ if (scumm_stricmp(_surfaces[i]->getFileName(), filename.c_str()) == 0) {
+ _surfaces[i]->_referenceCount++;
+ return _surfaces[i];
+ }
+ }
+
+ if (!BaseFileManager::getEngineInstance()->hasFile(filename)) {
+ if (filename.size()) {
+ _gameRef->LOG(0, "Missing image: '%s'", filename.c_str());
+ }
+ if (_gameRef->_debugDebugMode) {
+ return addSurface("invalid_debug.bmp", defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded);
+ } else {
+ return addSurface("invalid.bmp", defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded);
+ }
+ }
+
+ BaseSurface *surface;
+ surface = _gameRef->_renderer->createSurface();
+
+ if (!surface) {
+ return NULL;
+ }
+
+ if (DID_FAIL(surface->create(filename, defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded))) {
+ delete surface;
+ return NULL;
+ } else {
+ surface->_referenceCount = 1;
+ _surfaces.push_back(surface);
+ return surface;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSurfaceStorage::restoreAll() {
+ bool ret;
+ for (uint32 i = 0; i < _surfaces.size(); i++) {
+ ret = _surfaces[i]->restore();
+ if (ret != STATUS_OK) {
+ _gameRef->LOG(0, "BaseSurfaceStorage::RestoreAll failed");
+ return ret;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+/*
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceStorage::persist(BasePersistenceManager *persistMgr)
+{
+
+ if (!persistMgr->getIsSaving()) cleanup(false);
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ //_surfaces.persist(persistMgr);
+
+ return STATUS_OK;
+}
+*/
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceStorage::sortSurfaces() {
+ Common::sort(_surfaces.begin(), _surfaces.end(), surfaceSortCB);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseSurfaceStorage::surfaceSortCB(const void *arg1, const void *arg2) {
+ const BaseSurface *s1 = *((const BaseSurface *const *)arg1);
+ const BaseSurface *s2 = *((const BaseSurface *const *)arg2);
+
+ // sort by life time
+ if (s1->_lifeTime <= 0 && s2->_lifeTime > 0) {
+ return 1;
+ } else if (s1->_lifeTime > 0 && s2->_lifeTime <= 0) {
+ return -1;
+ }
+
+
+ // sort by validity
+ if (s1->_valid && !s2->_valid) {
+ return -1;
+ } else if (!s1->_valid && s2->_valid) {
+ return 1;
+ }
+
+ // sort by time
+ else if (s1->_lastUsedTime > s2->_lastUsedTime) {
+ return 1;
+ } else if (s1->_lastUsedTime < s2->_lastUsedTime) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_surface_storage.h b/engines/wintermute/base/base_surface_storage.h
new file mode 100644
index 0000000000..aef8ad23f9
--- /dev/null
+++ b/engines/wintermute/base/base_surface_storage.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SURFACE_STORAGE_H
+#define WINTERMUTE_BASE_SURFACE_STORAGE_H
+
+#include "engines/wintermute/base/base.h"
+#include "common/array.h"
+
+namespace Wintermute {
+class BaseSurface;
+class BaseSurfaceStorage : public BaseClass {
+public:
+ uint32 _lastCleanupTime;
+ bool initLoop();
+ bool sortSurfaces();
+ static int surfaceSortCB(const void *arg1, const void *arg2);
+ bool cleanup(bool warn = false);
+ //DECLARE_PERSISTENT(BaseSurfaceStorage, BaseClass);
+
+ bool restoreAll();
+ BaseSurface *addSurface(const Common::String &filename, bool defaultCK = true, byte ckRed = 0, byte ckGreen = 0, byte ckBlue = 0, int lifeTime = -1, bool keepLoaded = false);
+ bool removeSurface(BaseSurface *surface);
+ BaseSurfaceStorage(BaseGame *inGame);
+ virtual ~BaseSurfaceStorage();
+
+ Common::Array<BaseSurface *> _surfaces;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_transition_manager.cpp b/engines/wintermute/base/base_transition_manager.cpp
new file mode 100644
index 0000000000..7785f3d5af
--- /dev/null
+++ b/engines/wintermute/base/base_transition_manager.cpp
@@ -0,0 +1,137 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_transition_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+BaseTransitionMgr::BaseTransitionMgr(BaseGame *inGame) : BaseClass(inGame) {
+ _state = TRANS_MGR_READY;
+ _type = TRANSITION_NONE;
+ _origInteractive = false;
+ _preserveInteractive = false;
+ _lastTime = 0;
+ _started = false;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseTransitionMgr::~BaseTransitionMgr() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseTransitionMgr::isReady() {
+ return (_state == TRANS_MGR_READY);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseTransitionMgr::start(TTransitionType type, bool nonInteractive) {
+ if (_state != TRANS_MGR_READY) {
+ return STATUS_OK;
+ }
+
+ if (type == TRANSITION_NONE || type >= NUM_TRANSITION_TYPES) {
+ _state = TRANS_MGR_READY;
+ return STATUS_OK;
+ }
+
+ if (nonInteractive) {
+ _preserveInteractive = true;
+ _origInteractive = _gameRef->_interactive;
+ _gameRef->_interactive = false;
+ } /*else _preserveInteractive */;
+
+
+ _type = type;
+ _state = TRANS_MGR_RUNNING;
+ _started = false;
+
+ return STATUS_OK;
+}
+
+#define FADE_DURATION 200
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseTransitionMgr::update() {
+ if (isReady()) {
+ return STATUS_OK;
+ }
+
+ if (!_started) {
+ _started = true;
+ _lastTime = g_system->getMillis();
+ }
+
+ switch (_type) {
+ case TRANSITION_NONE:
+ _state = TRANS_MGR_READY;
+ break;
+
+ case TRANSITION_FADE_OUT: {
+ uint32 time = g_system->getMillis() - _lastTime;
+ int alpha = (int)(255 - (float)time / (float)FADE_DURATION * 255);
+ alpha = MIN(255, MAX(alpha, 0));
+ _gameRef->_renderer->fade((uint16)alpha);
+
+ if (time > FADE_DURATION) {
+ _state = TRANS_MGR_READY;
+ }
+ }
+ break;
+
+ case TRANSITION_FADE_IN: {
+ uint32 time = g_system->getMillis() - _lastTime;
+ int alpha = (int)((float)time / (float)FADE_DURATION * 255);
+ alpha = MIN(255, MAX(alpha, 0));
+ _gameRef->_renderer->fade((uint16)alpha);
+
+ if (time > FADE_DURATION) {
+ _state = TRANS_MGR_READY;
+ }
+ }
+ break;
+ default:
+ error("BaseTransitionMgr::Update - unhandled enum NUM_TRANSITION_TYPES");
+ }
+
+ if (isReady()) {
+ if (_preserveInteractive) {
+ _gameRef->_interactive = _origInteractive;
+ }
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_transition_manager.h b/engines/wintermute/base/base_transition_manager.h
new file mode 100644
index 0000000000..d16a44c88e
--- /dev/null
+++ b/engines/wintermute/base/base_transition_manager.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_TRANSITION_MANAGER_H
+#define WINTERMUTE_BASE_TRANSITION_MANAGER_H
+
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+class BaseTransitionMgr : public BaseClass {
+public:
+ bool _started;
+ uint32 _lastTime;
+ bool _origInteractive;
+ bool _preserveInteractive;
+ bool update();
+ bool start(TTransitionType type, bool nonInteractive = false);
+ bool isReady();
+ TTransMgrState _state;
+ BaseTransitionMgr(BaseGame *inGame);
+ virtual ~BaseTransitionMgr();
+ TTransitionType _type;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/base_viewport.cpp b/engines/wintermute/base/base_viewport.cpp
new file mode 100644
index 0000000000..7ec995449f
--- /dev/null
+++ b/engines/wintermute/base/base_viewport.cpp
@@ -0,0 +1,99 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/base_viewport.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseViewport, false)
+
+//////////////////////////////////////////////////////////////////////////
+BaseViewport::BaseViewport(BaseGame *inGame) : BaseClass(inGame) {
+ BasePlatform::setRectEmpty(&_rect);
+ _mainObject = NULL;
+ _offsetX = _offsetY = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseViewport::~BaseViewport() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseViewport::persist(BasePersistenceManager *persistMgr) {
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER(_mainObject));
+ persistMgr->transfer(TMEMBER(_offsetX));
+ persistMgr->transfer(TMEMBER(_offsetY));
+ persistMgr->transfer(TMEMBER(_rect));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseViewport::setRect(int left, int top, int right, int bottom, bool noCheck) {
+ if (!noCheck) {
+ left = MAX(left, 0);
+ top = MAX(top, 0);
+ right = MIN(right, _gameRef->_renderer->_width);
+ bottom = MIN(bottom, _gameRef->_renderer->_height);
+ }
+
+ BasePlatform::setRect(&_rect, left, top, right, bottom);
+ _offsetX = left;
+ _offsetY = top;
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+Rect32 *BaseViewport::getRect() {
+ return &_rect;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseViewport::getWidth() {
+ return _rect.right - _rect.left;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseViewport::getHeight() {
+ return _rect.bottom - _rect.top;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/base_viewport.h b/engines/wintermute/base/base_viewport.h
new file mode 100644
index 0000000000..98ad1c1e14
--- /dev/null
+++ b/engines/wintermute/base/base_viewport.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_VIEWPORT_H
+#define WINTERMUTE_BASE_VIEWPORT_H
+
+
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+class BaseObject;
+class BaseViewport : public BaseClass {
+public:
+ int getHeight();
+ int getWidth();
+ Rect32 *getRect();
+ bool setRect(int left, int top, int right, int bottom, bool noCheck = false);
+ DECLARE_PERSISTENT(BaseViewport, BaseClass)
+ int _offsetY;
+ int _offsetX;
+ BaseObject *_mainObject;
+ BaseViewport(BaseGame *inGame = NULL);
+ virtual ~BaseViewport();
+private:
+ Rect32 _rect;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/file/base_disk_file.cpp b/engines/wintermute/base/file/base_disk_file.cpp
new file mode 100644
index 0000000000..25be3dad2d
--- /dev/null
+++ b/engines/wintermute/base/file/base_disk_file.cpp
@@ -0,0 +1,194 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/dcgf.h"
+#include "engines/wintermute/base/file/base_disk_file.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "common/stream.h"
+#include "common/memstream.h"
+#include "common/file.h"
+#include "common/zlib.h"
+#include "common/archive.h"
+#include "common/tokenizer.h"
+#include "common/config-manager.h"
+
+namespace Wintermute {
+
+void correctSlashes(char *fileName) {
+ for (size_t i = 0; i < strlen(fileName); i++) {
+ if (fileName[i] == '\\') {
+ fileName[i] = '/';
+ }
+ }
+}
+
+// Parse a relative path in the game-folder, and if it exists, return a FSNode to it.
+static Common::FSNode getNodeForRelativePath(const Common::String &filename) {
+ // The filename can be an explicit path, thus we need to chop it up, expecting the path the game
+ // specifies to follow the Windows-convention of folder\subfolder\file (absolute paths should not happen)
+
+ // Absolute path: These should have been handled in openDiskFile.
+ if (filename.contains(':')) {
+ // So just return an invalid node.
+ return Common::FSNode();
+ }
+
+ // Relative path:
+ if (filename.contains('\\')) {
+ Common::StringTokenizer path(filename, "\\");
+
+ // Start traversing relative to the game-data-dir
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ Common::FSNode curNode = gameDataDir;
+
+ // Parse all path-elements
+ while (!path.empty()) {
+ // Get the next path-component by slicing on '\\'
+ Common::String pathPart = path.nextToken();
+ // Get the next FSNode in the chain, if it exists as a child from the previous.
+ curNode = curNode.getChild(pathPart);
+ if (!curNode.isReadable()) {
+ // Return an invalid FSNode.
+ return Common::FSNode();
+ }
+ // Following the comments in common/fs.h, anything not a directory is a file.
+ if (!curNode.isDirectory()) {
+ if (!path.empty()) {
+ error("Relative path %s reached a file before the end of the path", filename.c_str());
+ }
+ return curNode;
+ }
+ }
+ }
+ // Return an invalid FSNode to mark that we didn't find the requested file.
+ return Common::FSNode();
+}
+
+bool diskFileExists(const Common::String &filename) {
+ // Try directly from SearchMan first
+ Common::ArchiveMemberList files;
+ SearchMan.listMatchingMembers(files, filename);
+
+ for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) {
+ if ((*it)->getName() == filename) {
+ return true;
+ }
+ }
+ // File wasn't found in SearchMan, try to parse the path as a relative path.
+ Common::FSNode searchNode = getNodeForRelativePath(filename);
+ if (searchNode.exists() && !searchNode.isDirectory() && searchNode.isReadable()) {
+ return true;
+ }
+ return false;
+}
+
+Common::SeekableReadStream *openDiskFile(const Common::String &filename) {
+ uint32 prefixSize = 0;
+ Common::SeekableReadStream *file = NULL;
+ Common::String fixedFilename = filename;
+
+ // Absolute path: TODO: Add specific fallbacks here.
+ if (filename.contains(':')) {
+ if (filename.hasPrefix("c:\\windows\\fonts\\")) { // East Side Story refers to "c:\windows\fonts\framd.ttf"
+ fixedFilename = filename.c_str() + 17;
+ } else {
+ error("openDiskFile::Absolute path or invalid filename used in %s", filename.c_str());
+ }
+ }
+ // Try directly from SearchMan first
+ Common::ArchiveMemberList files;
+ SearchMan.listMatchingMembers(files, fixedFilename);
+
+ for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) {
+ if ((*it)->getName() == filename) {
+ file = (*it)->createReadStream();
+ break;
+ }
+ }
+ // File wasn't found in SearchMan, try to parse the path as a relative path.
+ if (!file) {
+ Common::FSNode searchNode = getNodeForRelativePath(filename);
+ if (searchNode.exists() && !searchNode.isDirectory() && searchNode.isReadable()) {
+ file = searchNode.createReadStream();
+ }
+ }
+ if (file) {
+ uint32 magic1, magic2;
+ magic1 = file->readUint32LE();
+ magic2 = file->readUint32LE();
+
+ bool compressed = false;
+ if (magic1 == DCGF_MAGIC && magic2 == COMPRESSED_FILE_MAGIC) {
+ compressed = true;
+ }
+
+ if (compressed) {
+ uint32 dataOffset, compSize, uncompSize;
+ dataOffset = file->readUint32LE();
+ compSize = file->readUint32LE();
+ uncompSize = file->readUint32LE();
+
+ byte *compBuffer = new byte[compSize];
+ if (!compBuffer) {
+ error("Error allocating memory for compressed file '%s'", filename.c_str());
+ delete file;
+ return NULL;
+ }
+
+ byte *data = new byte[uncompSize];
+ if (!data) {
+ error("Error allocating buffer for file '%s'", filename.c_str());
+ delete[] compBuffer;
+ delete file;
+ return NULL;
+ }
+ file->seek(dataOffset + prefixSize, SEEK_SET);
+ file->read(compBuffer, compSize);
+
+ if (Common::uncompress(data, (unsigned long *)&uncompSize, compBuffer, compSize) != true) {
+ error("Error uncompressing file '%s'", filename.c_str());
+ delete[] compBuffer;
+ delete file;
+ return NULL;
+ }
+
+ delete[] compBuffer;
+ delete file;
+ return new Common::MemoryReadStream(data, uncompSize, DisposeAfterUse::YES);
+ } else {
+ file->seek(0, SEEK_SET);
+ return file;
+ }
+
+ return file;
+
+ }
+ return NULL;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/file/base_disk_file.h b/engines/wintermute/base/file/base_disk_file.h
new file mode 100644
index 0000000000..c9f93b80d9
--- /dev/null
+++ b/engines/wintermute/base/file/base_disk_file.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_DISKFILE_H
+#define WINTERMUTE_BASE_DISKFILE_H
+
+#include "common/stream.h"
+
+namespace Wintermute {
+
+Common::SeekableReadStream *openDiskFile(const Common::String &filename);
+bool diskFileExists(const Common::String &filename);
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/file/base_file.cpp b/engines/wintermute/base/file/base_file.cpp
new file mode 100644
index 0000000000..f52a13211e
--- /dev/null
+++ b/engines/wintermute/base/file/base_file.cpp
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/file/base_file.h"
+#include "common/memstream.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseFile::BaseFile() {
+ _pos = 0;
+ _size = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseFile::~BaseFile() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFile::isEOF() {
+ return _pos == _size;
+}
+
+Common::SeekableReadStream *BaseFile::getMemStream() {
+ uint32 oldPos = getPos();
+ seek(0);
+ byte *data = new byte[getSize()];
+ read(data, getSize());
+ seek(oldPos);
+ Common::MemoryReadStream *memStream = new Common::MemoryReadStream(data, getSize(), DisposeAfterUse::YES);
+ return memStream;
+}
+
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/file/base_file.h b/engines/wintermute/base/file/base_file.h
new file mode 100644
index 0000000000..82f6ce3554
--- /dev/null
+++ b/engines/wintermute/base/file/base_file.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FILE_H
+#define WINTERMUTE_BASE_FILE_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "common/str.h"
+#include "common/stream.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Wintermute {
+
+class BaseFile {
+protected:
+ uint32 _pos;
+ uint32 _size;
+public:
+ virtual uint32 getSize() {
+ return _size;
+ };
+ virtual uint32 getPos() {
+ return _pos;
+ };
+ virtual bool seek(uint32 pos, int whence = SEEK_SET) = 0;
+ virtual bool read(void *buffer, uint32 size) = 0;
+ virtual bool close() = 0;
+ virtual bool open(const Common::String &filename) = 0;
+ virtual bool isEOF();
+ BaseFile();
+ virtual ~BaseFile();
+ // Temporary solution to allow usage in ScummVM-code:
+ virtual Common::SeekableReadStream *getMemStream();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/file/base_file_entry.cpp b/engines/wintermute/base/file/base_file_entry.cpp
new file mode 100644
index 0000000000..b9805d78dd
--- /dev/null
+++ b/engines/wintermute/base/file/base_file_entry.cpp
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/file/base_file_entry.h"
+#include "engines/wintermute/base/file/base_package.h"
+#include "common/stream.h"
+#include "common/substream.h"
+#include "common/zlib.h"
+
+namespace Wintermute {
+
+Common::SeekableReadStream *BaseFileEntry::createReadStream() const {
+ Common::SeekableReadStream *file = _package->getFilePointer();
+ if (!file) {
+ return NULL;
+ }
+
+ bool compressed = (_compressedLength != 0);
+
+ if (compressed) {
+ file = Common::wrapCompressedReadStream(new Common::SeekableSubReadStream(file, _offset, _offset + _length, DisposeAfterUse::YES), _length); //
+ } else {
+ file = new Common::SeekableSubReadStream(file, _offset, _offset + _length, DisposeAfterUse::YES);
+ }
+
+ file->seek(0);
+
+ return file;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseFileEntry::BaseFileEntry() {
+ _package = NULL;
+ _length = _compressedLength = _offset = _flags = 0;
+ _filename = "";
+
+ _timeDate1 = _timeDate2 = 0;
+
+ _journalTime = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseFileEntry::~BaseFileEntry() {
+ _package = NULL; // ref only
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/file/base_file_entry.h b/engines/wintermute/base/file/base_file_entry.h
new file mode 100644
index 0000000000..6e4823d994
--- /dev/null
+++ b/engines/wintermute/base/file/base_file_entry.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FILEENTRY_H
+#define WINTERMUTE_BASE_FILEENTRY_H
+
+#include "common/archive.h"
+#include "common/str.h"
+#include "common/stream.h"
+
+namespace Wintermute {
+
+class BasePackage;
+
+class BaseFileEntry : public Common::ArchiveMember {
+public:
+ virtual Common::SeekableReadStream *createReadStream() const;
+ virtual Common::String getName() const { return _filename; }
+ uint32 _timeDate2;
+ uint32 _timeDate1;
+ uint32 _flags;
+ uint32 _journalTime;
+ Common::String _filename;
+ uint32 _compressedLength;
+ uint32 _length;
+ uint32 _offset;
+ BasePackage *_package;
+ BaseFileEntry();
+ virtual ~BaseFileEntry();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/file/base_package.cpp b/engines/wintermute/base/file/base_package.cpp
new file mode 100644
index 0000000000..51a1558a7c
--- /dev/null
+++ b/engines/wintermute/base/file/base_package.cpp
@@ -0,0 +1,276 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/file/base_package.h"
+#include "engines/wintermute/base/file/base_file_entry.h"
+#include "engines/wintermute/base/file/dcpackage.h"
+#include "engines/wintermute/wintermute.h"
+#include "common/file.h"
+#include "common/stream.h"
+#include "common/debug.h"
+
+namespace Wintermute {
+
+BasePackage::BasePackage() {
+ _name = "";
+ _cd = 0;
+ _priority = 0;
+ _boundToExe = false;
+}
+
+Common::SeekableReadStream *BasePackage::getFilePointer() {
+ Common::SeekableReadStream *stream = _fsnode.createReadStream();
+
+ return stream;
+}
+
+static bool findPackageSignature(Common::SeekableReadStream *f, uint32 *offset) {
+ byte buf[32768];
+
+ byte signature[8];
+ ((uint32 *)signature)[0] = PACKAGE_MAGIC_1;
+ ((uint32 *)signature)[1] = PACKAGE_MAGIC_2;
+
+ uint32 fileSize = (uint32)f->size();
+ uint32 startPos = 1024 * 1024;
+ uint32 bytesRead = startPos;
+
+ while (bytesRead < fileSize - 16) {
+ uint32 toRead = MIN((unsigned int)32768, fileSize - bytesRead);
+ f->seek((int32)startPos, SEEK_SET);
+ uint32 actuallyRead = f->read(buf, toRead);
+ if (actuallyRead != toRead) {
+ return false;
+ }
+
+ for (uint32 i = 0; i < toRead - 8; i++)
+ if (!memcmp(buf + i, signature, 8)) {
+ *offset = startPos + i;
+ return true;
+ }
+
+ bytesRead = bytesRead + toRead - 16;
+ startPos = startPos + toRead - 16;
+
+ }
+ return false;
+
+}
+
+void TPackageHeader::readFromStream(Common::ReadStream *stream) {
+ _magic1 = stream->readUint32LE();
+ _magic2 = stream->readUint32LE();
+ _packageVersion = stream->readUint32LE();
+
+ _gameVersion = stream->readUint32LE();
+
+ _priority = stream->readByte();
+ _cd = stream->readByte();
+ _masterIndex = stream->readByte();
+ stream->readByte(); // To align the next byte...
+
+ _creationTime = stream->readUint32LE();
+
+ stream->read(_desc, 100);
+ _numDirs = stream->readUint32LE();
+}
+
+PackageSet::PackageSet(Common::FSNode file, const Common::String &filename, bool searchSignature) {
+ uint32 absoluteOffset = 0;
+ _priority = 0;
+ bool boundToExe = false;
+ Common::SeekableReadStream *stream = file.createReadStream();
+ if (!stream) {
+ return;
+ }
+ if (searchSignature) {
+ uint32 offset;
+ if (!findPackageSignature(stream, &offset)) {
+ delete stream;
+ return;
+ } else {
+ stream->seek(offset, SEEK_SET);
+ absoluteOffset = offset;
+ boundToExe = true;
+ }
+ }
+
+ TPackageHeader hdr;
+ hdr.readFromStream(stream);
+ if (hdr._magic1 != PACKAGE_MAGIC_1 || hdr._magic2 != PACKAGE_MAGIC_2 || hdr._packageVersion > PACKAGE_VERSION) {
+ debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Invalid header in package file '%s'. Ignoring.", filename.c_str());
+ delete stream;
+ return;
+ }
+
+ if (hdr._packageVersion != PACKAGE_VERSION) {
+ debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Warning: package file '%s' is outdated.", filename.c_str());
+ }
+ _priority = hdr._priority;
+ // new in v2
+ if (hdr._packageVersion == PACKAGE_VERSION) {
+ uint32 dirOffset;
+ dirOffset = stream->readUint32LE();
+ dirOffset += absoluteOffset;
+ stream->seek(dirOffset, SEEK_SET);
+ }
+ assert(hdr._numDirs == 1);
+ for (uint32 i = 0; i < hdr._numDirs; i++) {
+ BasePackage *pkg = new BasePackage();
+ if (!pkg) {
+ return;
+ }
+ pkg->_fsnode = file;
+
+ pkg->_boundToExe = boundToExe;
+
+ // read package info
+ byte nameLength = stream->readByte();
+ char *pkgName = new char[nameLength];
+ stream->read(pkgName, nameLength);
+ pkg->_name = pkgName;
+ pkg->_cd = stream->readByte();
+ pkg->_priority = hdr._priority;
+ delete[] pkgName;
+ pkgName = NULL;
+
+ if (!hdr._masterIndex) {
+ pkg->_cd = 0; // override CD to fixed disk
+ }
+ _packages.push_back(pkg);
+
+ // read file entries
+ uint32 numFiles = stream->readUint32LE();
+
+ for (uint32 j = 0; j < numFiles; j++) {
+ char *name;
+ uint32 offset, length, compLength, flags;/*, timeDate1, timeDate2;*/
+
+ nameLength = stream->readByte();
+ name = new char[nameLength];
+ stream->read(name, nameLength);
+
+ // v2 - xor name
+ if (hdr._packageVersion == PACKAGE_VERSION) {
+ for (int k = 0; k < nameLength; k++) {
+ ((byte *)name)[k] ^= 'D';
+ }
+ }
+ debugC(kWintermuteDebugFileAccess, "Package contains %s", name);
+
+ Common::String upcName = name;
+ upcName.toUppercase();
+ delete[] name;
+ name = NULL;
+
+ offset = stream->readUint32LE();
+ offset += absoluteOffset;
+ length = stream->readUint32LE();
+ compLength = stream->readUint32LE();
+ flags = stream->readUint32LE();
+
+ if (hdr._packageVersion == PACKAGE_VERSION) {
+ /* timeDate1 = */ stream->readUint32LE();
+ /* timeDate2 = */ stream->readUint32LE();
+ }
+ _filesIter = _files.find(upcName);
+ if (_filesIter == _files.end()) {
+ BaseFileEntry *fileEntry = new BaseFileEntry();
+ fileEntry->_package = pkg;
+ fileEntry->_offset = offset;
+ fileEntry->_length = length;
+ fileEntry->_compressedLength = compLength;
+ fileEntry->_flags = flags;
+
+ _files[upcName] = Common::ArchiveMemberPtr(fileEntry);
+ } else {
+ // current package has higher priority than the registered
+ // TODO: This cast might be a bit ugly.
+ BaseFileEntry *filePtr = (BaseFileEntry *) &*(_filesIter->_value);
+ if (pkg->_priority > filePtr->_package->_priority) {
+ filePtr->_package = pkg;
+ filePtr->_offset = offset;
+ filePtr->_length = length;
+ filePtr->_compressedLength = compLength;
+ filePtr->_flags = flags;
+ }
+ }
+ }
+ }
+ debugC(kWintermuteDebugFileAccess, " Registered %d files in %d package(s)", _files.size(), _packages.size());
+
+ delete stream;
+}
+
+PackageSet::~PackageSet() {
+ for (Common::Array<BasePackage *>::iterator it = _packages.begin(); it != _packages.end(); ++it) {
+ delete *it;
+ }
+ _packages.clear();
+}
+
+bool PackageSet::hasFile(const Common::String &name) const {
+ Common::String upcName = name;
+ upcName.toUppercase();
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it;
+ it = _files.find(upcName.c_str());
+ return (it != _files.end());
+}
+
+int PackageSet::listMembers(Common::ArchiveMemberList &list) const {
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it = _files.begin();
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator end = _files.end();
+ int count = 0;
+ for (; it != end; ++it) {
+ const Common::ArchiveMemberPtr ptr(it->_value);
+ list.push_back(ptr);
+ count++;
+ }
+ return count;
+}
+
+const Common::ArchiveMemberPtr PackageSet::getMember(const Common::String &name) const {
+ Common::String upcName = name;
+ upcName.toUppercase();
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it;
+ it = _files.find(upcName.c_str());
+ return Common::ArchiveMemberPtr(it->_value);
+}
+
+Common::SeekableReadStream *PackageSet::createReadStreamForMember(const Common::String &name) const {
+ Common::String upcName = name;
+ upcName.toUppercase();
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it;
+ it = _files.find(upcName.c_str());
+ if (it != _files.end()) {
+ return it->_value->createReadStream();
+ }
+ return NULL;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/file/base_package.h b/engines/wintermute/base/file/base_package.h
new file mode 100644
index 0000000000..2882eb03b7
--- /dev/null
+++ b/engines/wintermute/base/file/base_package.h
@@ -0,0 +1,90 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_PACKAGE_H
+#define WINTERMUTE_BASE_PACKAGE_H
+
+#include "common/archive.h"
+#include "common/stream.h"
+#include "common/fs.h"
+
+namespace Wintermute {
+class BasePackage {
+public:
+ Common::SeekableReadStream *getFilePointer();
+ Common::FSNode _fsnode;
+ bool _boundToExe;
+ byte _priority;
+ Common::String _name;
+ int _cd;
+ BasePackage();
+};
+
+class PackageSet : public Common::Archive {
+public:
+ virtual ~PackageSet();
+
+ PackageSet(Common::FSNode package, const Common::String &filename = "", bool searchSignature = false);
+ /**
+ * Check if a member with the given name is present in the Archive.
+ * Patterns are not allowed, as this is meant to be a quick File::exists()
+ * replacement.
+ */
+ virtual bool hasFile(const Common::String &name) const;
+
+ /**
+ * Add all members of the Archive to list.
+ * Must only append to list, and not remove elements from it.
+ *
+ * @return the number of names added to list
+ */
+ virtual int listMembers(Common::ArchiveMemberList &list) const;
+
+ /**
+ * Returns a ArchiveMember representation of the given file.
+ */
+ virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
+
+ /**
+ * Create a stream bound to a member with the specified name in the
+ * archive. If no member with this name exists, 0 is returned.
+ * @return the newly created input stream
+ */
+ virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+ int getPriority() const { return _priority; }
+private:
+ byte _priority;
+ Common::Array<BasePackage *> _packages;
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr> _files;
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr>::iterator _filesIter;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/file/base_resources.cpp b/engines/wintermute/base/file/base_resources.cpp
new file mode 100644
index 0000000000..0b32cb0c4f
--- /dev/null
+++ b/engines/wintermute/base/file/base_resources.cpp
@@ -0,0 +1,2830 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/file/base_resources.h"
+#include "common/str.h"
+#include "common/memstream.h"
+
+namespace Wintermute {
+
+unsigned char invalid[] = {
+ 0x42, 0x4d, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x4e, 0x00, 0x00, 0x20, 0x4e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+} ;
+
+unsigned char invaliddebug[] = {
+ 0x42, 0x4d, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80,
+ 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0x00, 0xc0, 0xdc, 0xc0, 0x00, 0xf0, 0xca, 0xa6, 0x00, 0x00, 0x20,
+ 0x40, 0x00, 0x00, 0x20, 0x60, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0xa0, 0x00, 0x00, 0x20,
+ 0xc0, 0x00, 0x00, 0x20, 0xe0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x40,
+ 0x40, 0x00, 0x00, 0x40, 0x60, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x40,
+ 0xc0, 0x00, 0x00, 0x40, 0xe0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0x60,
+ 0x40, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x80, 0x00, 0x00, 0x60, 0xa0, 0x00, 0x00, 0x60,
+ 0xc0, 0x00, 0x00, 0x60, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80,
+ 0x40, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x80,
+ 0xc0, 0x00, 0x00, 0x80, 0xe0, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x20, 0x00, 0x00, 0xa0,
+ 0x40, 0x00, 0x00, 0xa0, 0x60, 0x00, 0x00, 0xa0, 0x80, 0x00, 0x00, 0xa0, 0xa0, 0x00, 0x00, 0xa0,
+ 0xc0, 0x00, 0x00, 0xa0, 0xe0, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0xc0,
+ 0x40, 0x00, 0x00, 0xc0, 0x60, 0x00, 0x00, 0xc0, 0x80, 0x00, 0x00, 0xc0, 0xa0, 0x00, 0x00, 0xc0,
+ 0xc0, 0x00, 0x00, 0xc0, 0xe0, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x20, 0x00, 0x00, 0xe0,
+ 0x40, 0x00, 0x00, 0xe0, 0x60, 0x00, 0x00, 0xe0, 0x80, 0x00, 0x00, 0xe0, 0xa0, 0x00, 0x00, 0xe0,
+ 0xc0, 0x00, 0x00, 0xe0, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x40, 0x00,
+ 0x40, 0x00, 0x40, 0x00, 0x60, 0x00, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xa0, 0x00, 0x40, 0x00,
+ 0xc0, 0x00, 0x40, 0x00, 0xe0, 0x00, 0x40, 0x20, 0x00, 0x00, 0x40, 0x20, 0x20, 0x00, 0x40, 0x20,
+ 0x40, 0x00, 0x40, 0x20, 0x60, 0x00, 0x40, 0x20, 0x80, 0x00, 0x40, 0x20, 0xa0, 0x00, 0x40, 0x20,
+ 0xc0, 0x00, 0x40, 0x20, 0xe0, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x20, 0x00, 0x40, 0x40,
+ 0x40, 0x00, 0x40, 0x40, 0x60, 0x00, 0x40, 0x40, 0x80, 0x00, 0x40, 0x40, 0xa0, 0x00, 0x40, 0x40,
+ 0xc0, 0x00, 0x40, 0x40, 0xe0, 0x00, 0x40, 0x60, 0x00, 0x00, 0x40, 0x60, 0x20, 0x00, 0x40, 0x60,
+ 0x40, 0x00, 0x40, 0x60, 0x60, 0x00, 0x40, 0x60, 0x80, 0x00, 0x40, 0x60, 0xa0, 0x00, 0x40, 0x60,
+ 0xc0, 0x00, 0x40, 0x60, 0xe0, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0x80, 0x20, 0x00, 0x40, 0x80,
+ 0x40, 0x00, 0x40, 0x80, 0x60, 0x00, 0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0xa0, 0x00, 0x40, 0x80,
+ 0xc0, 0x00, 0x40, 0x80, 0xe0, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x40, 0xa0, 0x20, 0x00, 0x40, 0xa0,
+ 0x40, 0x00, 0x40, 0xa0, 0x60, 0x00, 0x40, 0xa0, 0x80, 0x00, 0x40, 0xa0, 0xa0, 0x00, 0x40, 0xa0,
+ 0xc0, 0x00, 0x40, 0xa0, 0xe0, 0x00, 0x40, 0xc0, 0x00, 0x00, 0x40, 0xc0, 0x20, 0x00, 0x40, 0xc0,
+ 0x40, 0x00, 0x40, 0xc0, 0x60, 0x00, 0x40, 0xc0, 0x80, 0x00, 0x40, 0xc0, 0xa0, 0x00, 0x40, 0xc0,
+ 0xc0, 0x00, 0x40, 0xc0, 0xe0, 0x00, 0x40, 0xe0, 0x00, 0x00, 0x40, 0xe0, 0x20, 0x00, 0x40, 0xe0,
+ 0x40, 0x00, 0x40, 0xe0, 0x60, 0x00, 0x40, 0xe0, 0x80, 0x00, 0x40, 0xe0, 0xa0, 0x00, 0x40, 0xe0,
+ 0xc0, 0x00, 0x40, 0xe0, 0xe0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00,
+ 0x40, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xa0, 0x00, 0x80, 0x00,
+ 0xc0, 0x00, 0x80, 0x00, 0xe0, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, 0x20, 0x20, 0x00, 0x80, 0x20,
+ 0x40, 0x00, 0x80, 0x20, 0x60, 0x00, 0x80, 0x20, 0x80, 0x00, 0x80, 0x20, 0xa0, 0x00, 0x80, 0x20,
+ 0xc0, 0x00, 0x80, 0x20, 0xe0, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x40, 0x20, 0x00, 0x80, 0x40,
+ 0x40, 0x00, 0x80, 0x40, 0x60, 0x00, 0x80, 0x40, 0x80, 0x00, 0x80, 0x40, 0xa0, 0x00, 0x80, 0x40,
+ 0xc0, 0x00, 0x80, 0x40, 0xe0, 0x00, 0x80, 0x60, 0x00, 0x00, 0x80, 0x60, 0x20, 0x00, 0x80, 0x60,
+ 0x40, 0x00, 0x80, 0x60, 0x60, 0x00, 0x80, 0x60, 0x80, 0x00, 0x80, 0x60, 0xa0, 0x00, 0x80, 0x60,
+ 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x20, 0x00, 0x80, 0x80,
+ 0x40, 0x00, 0x80, 0x80, 0x60, 0x00, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0xa0, 0x00, 0x80, 0x80,
+ 0xc0, 0x00, 0x80, 0x80, 0xe0, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x80, 0xa0, 0x20, 0x00, 0x80, 0xa0,
+ 0x40, 0x00, 0x80, 0xa0, 0x60, 0x00, 0x80, 0xa0, 0x80, 0x00, 0x80, 0xa0, 0xa0, 0x00, 0x80, 0xa0,
+ 0xc0, 0x00, 0x80, 0xa0, 0xe0, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x80, 0xc0, 0x20, 0x00, 0x80, 0xc0,
+ 0x40, 0x00, 0x80, 0xc0, 0x60, 0x00, 0x80, 0xc0, 0x80, 0x00, 0x80, 0xc0, 0xa0, 0x00, 0x80, 0xc0,
+ 0xc0, 0x00, 0x80, 0xc0, 0xe0, 0x00, 0x80, 0xe0, 0x00, 0x00, 0x80, 0xe0, 0x20, 0x00, 0x80, 0xe0,
+ 0x40, 0x00, 0x80, 0xe0, 0x60, 0x00, 0x80, 0xe0, 0x80, 0x00, 0x80, 0xe0, 0xa0, 0x00, 0x80, 0xe0,
+ 0xc0, 0x00, 0x80, 0xe0, 0xe0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x20, 0x00, 0xc0, 0x00,
+ 0x40, 0x00, 0xc0, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x80, 0x00, 0xc0, 0x00, 0xa0, 0x00, 0xc0, 0x00,
+ 0xc0, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xc0, 0x20, 0x00, 0x00, 0xc0, 0x20, 0x20, 0x00, 0xc0, 0x20,
+ 0x40, 0x00, 0xc0, 0x20, 0x60, 0x00, 0xc0, 0x20, 0x80, 0x00, 0xc0, 0x20, 0xa0, 0x00, 0xc0, 0x20,
+ 0xc0, 0x00, 0xc0, 0x20, 0xe0, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xc0, 0x40, 0x20, 0x00, 0xc0, 0x40,
+ 0x40, 0x00, 0xc0, 0x40, 0x60, 0x00, 0xc0, 0x40, 0x80, 0x00, 0xc0, 0x40, 0xa0, 0x00, 0xc0, 0x40,
+ 0xc0, 0x00, 0xc0, 0x40, 0xe0, 0x00, 0xc0, 0x60, 0x00, 0x00, 0xc0, 0x60, 0x20, 0x00, 0xc0, 0x60,
+ 0x40, 0x00, 0xc0, 0x60, 0x60, 0x00, 0xc0, 0x60, 0x80, 0x00, 0xc0, 0x60, 0xa0, 0x00, 0xc0, 0x60,
+ 0xc0, 0x00, 0xc0, 0x60, 0xe0, 0x00, 0xc0, 0x80, 0x00, 0x00, 0xc0, 0x80, 0x20, 0x00, 0xc0, 0x80,
+ 0x40, 0x00, 0xc0, 0x80, 0x60, 0x00, 0xc0, 0x80, 0x80, 0x00, 0xc0, 0x80, 0xa0, 0x00, 0xc0, 0x80,
+ 0xc0, 0x00, 0xc0, 0x80, 0xe0, 0x00, 0xc0, 0xa0, 0x00, 0x00, 0xc0, 0xa0, 0x20, 0x00, 0xc0, 0xa0,
+ 0x40, 0x00, 0xc0, 0xa0, 0x60, 0x00, 0xc0, 0xa0, 0x80, 0x00, 0xc0, 0xa0, 0xa0, 0x00, 0xc0, 0xa0,
+ 0xc0, 0x00, 0xc0, 0xa0, 0xe0, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0x20, 0x00, 0xc0, 0xc0,
+ 0x40, 0x00, 0xc0, 0xc0, 0x60, 0x00, 0xc0, 0xc0, 0x80, 0x00, 0xc0, 0xc0, 0xa0, 0x00, 0xf0, 0xfb,
+ 0xff, 0x00, 0xa4, 0xa0, 0xa0, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff,
+ 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00,
+ 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9,
+ 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0xf9, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00,
+ 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9
+} ;
+
+unsigned char systemfont[] = {
+ 0x42, 0x4d, 0x36, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x70, 0x0b, 0x00, 0x00, 0x70, 0x0b, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x80, 0x80,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00,
+ 0x01, 0x02, 0x02, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00,
+ 0x02, 0x00, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x00, 0x02, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0x00, 0x01, 0x02, 0x00, 0x02, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00,
+ 0x02, 0x01, 0x00, 0x02, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02,
+ 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00,
+ 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x00,
+ 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02,
+ 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x01,
+ 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02,
+ 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+} ;
+
+Common::SeekableReadStream *BaseResources::getFile(const Common::String &filename) {
+ if (scumm_stricmp(filename.c_str(), "invalid.bmp") == 0) {
+ return new Common::MemoryReadStream(invalid, sizeof(invalid), DisposeAfterUse::NO);
+ } else if (scumm_stricmp(filename.c_str(), "invalid_debug.bmp") == 0) {
+ return new Common::MemoryReadStream(invaliddebug, sizeof(invalid), DisposeAfterUse::NO);
+ } else if (scumm_stricmp(filename.c_str(), "syste_font.bmp") == 0) {
+ return new Common::MemoryReadStream(systemfont, sizeof(invalid), DisposeAfterUse::NO);
+ }
+ return NULL;
+}
+
+bool BaseResources::hasFile(const Common::String &filename) {
+ if (scumm_stricmp(filename.c_str(), "invalid.bmp") == 0) {
+ return true;
+ } else if (scumm_stricmp(filename.c_str(), "invalid_debug.bmp") == 0) {
+ return true;
+ } else if (scumm_stricmp(filename.c_str(), "syste_font.bmp") == 0) {
+ return true;
+ }
+ return false;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/file/base_resources.h b/engines/wintermute/base/file/base_resources.h
new file mode 100644
index 0000000000..91c30bcfa7
--- /dev/null
+++ b/engines/wintermute/base/file/base_resources.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_RESOURCES_H
+#define WINTERMUTE_BASE_RESOURCES_H
+
+#include "common/stream.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+class BaseResources {
+public:
+ static Common::SeekableReadStream *getFile(const Common::String &filename);
+ static bool hasFile(const Common::String &filename);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/file/base_save_thumb_file.cpp b/engines/wintermute/base/file/base_save_thumb_file.cpp
new file mode 100644
index 0000000000..94d3e5a94e
--- /dev/null
+++ b/engines/wintermute/base/file/base_save_thumb_file.cpp
@@ -0,0 +1,153 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_persistence_manager.h"
+#include "engines/wintermute/base/file/base_save_thumb_file.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSaveThumbFile::BaseSaveThumbFile() {
+ _data = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSaveThumbFile::~BaseSaveThumbFile() {
+ close();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSaveThumbFile::open(const Common::String &filename) {
+ close();
+
+ if (scumm_strnicmp(filename.c_str(), "savegame:", 9) != 0) {
+ return STATUS_FAILED;
+ }
+
+ char *tempFilename = new char[strlen(filename.c_str()) - 8];
+ strcpy(tempFilename, filename.c_str() + 9);
+ for (uint32 i = 0; i < strlen(tempFilename); i++) {
+ if (tempFilename[i] < '0' || tempFilename[i] > '9') {
+ tempFilename[i] = '\0';
+ break;
+ }
+ }
+
+ // get slot number from name
+ int slot = atoi(tempFilename);
+ delete[] tempFilename;
+
+ BasePersistenceManager *pm = new BasePersistenceManager();
+ Common::String slotFilename = pm->getFilenameForSlot(slot);
+ if (!pm) {
+ return STATUS_FAILED;
+ }
+
+ if (DID_FAIL(pm->initLoad(slotFilename))) {
+ delete pm;
+ return STATUS_FAILED;
+ }
+
+ bool res;
+
+ if (pm->_thumbnailDataSize != 0) {
+ _data = new byte[pm->_thumbnailDataSize];
+ memcpy(_data, pm->_thumbnailData, pm->_thumbnailDataSize);
+ _size = pm->_thumbnailDataSize;
+ res = STATUS_OK;
+ } else {
+ res = STATUS_FAILED;
+ }
+ delete pm;
+
+ return res;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSaveThumbFile::close() {
+ delete[] _data;
+ _data = NULL;
+
+ _pos = 0;
+ _size = 0;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSaveThumbFile::read(void *buffer, uint32 size) {
+ if (!_data || _pos + size > _size) {
+ return STATUS_FAILED;
+ }
+
+ memcpy(buffer, (byte *)_data + _pos, size);
+ _pos += size;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSaveThumbFile::seek(uint32 pos, int whence) {
+ if (!_data) {
+ return STATUS_FAILED;
+ }
+
+ uint32 newPos = 0;
+
+ switch (whence) {
+ case SEEK_SET:
+ newPos = pos;
+ break;
+ case SEEK_END:
+ newPos = _size + pos;
+ break;
+ case SEEK_CUR:
+ newPos = _pos + pos;
+ break;
+ }
+
+ if (newPos > _size) {
+ return STATUS_FAILED;
+ } else {
+ _pos = newPos;
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/file/base_save_thumb_file.h b/engines/wintermute/base/file/base_save_thumb_file.h
new file mode 100644
index 0000000000..3b217525fd
--- /dev/null
+++ b/engines/wintermute/base/file/base_save_thumb_file.h
@@ -0,0 +1,52 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SAVETHUMBFILE_H
+#define WINTERMUTE_BASE_SAVETHUMBFILE_H
+
+
+#include "engines/wintermute/base/file/base_file.h"
+
+namespace Wintermute {
+
+//TODO: Get rid of this
+class BaseSaveThumbFile : public BaseFile {
+public:
+ BaseSaveThumbFile();
+ virtual ~BaseSaveThumbFile();
+ virtual bool seek(uint32 pos, int whence = SEEK_SET);
+ virtual bool read(void *buffer, uint32 size);
+ virtual bool close();
+ virtual bool open(const Common::String &filename);
+private:
+ byte *_data;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/file/dcpackage.h b/engines/wintermute/base/file/dcpackage.h
new file mode 100644
index 0000000000..2234139408
--- /dev/null
+++ b/engines/wintermute/base/file/dcpackage.h
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef _DCPACKAGE_H_
+#define _DCPACKAGE_H_
+
+
+#define PACKAGE_MAGIC_1 0xDEC0ADDE
+#define PACKAGE_MAGIC_2 0x4B4E554A // "JUNK"
+#define PACKAGE_VERSION 0x00000200
+#define PACKAGE_EXTENSION "dcp"
+
+#include "common/stream.h"
+
+namespace Wintermute {
+
+struct TPackageHeader {
+ uint32 _magic1;
+ uint32 _magic2;
+ uint32 _packageVersion;
+ uint32 _gameVersion;
+ byte _priority;
+ byte _cd;
+ bool _masterIndex;
+ uint32 _creationTime;
+ char _desc[100];
+ uint32 _numDirs;
+ // base_package.cpp:
+ void readFromStream(Common::ReadStream *stream);
+};
+
+/*
+v2: uint32 DirOffset
+
+
+Dir: byte NameLength
+ char Name [NameLength]
+ byte CD;
+ uint32 NumEntries
+
+
+Entry: byte NameLength
+ char Name [NameLength]
+ uint32 Offset
+ uint32 Length
+ uint32 CompLength
+ uint32 Flags
+v2: uint32 TimeDate1
+ uint32 TimeDate2 // not used
+
+*/
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/font/base_font.cpp b/engines/wintermute/base/font/base_font.cpp
new file mode 100644
index 0000000000..87dd3da5a3
--- /dev/null
+++ b/engines/wintermute/base/font/base_font.cpp
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/font/base_font_bitmap.h"
+#include "engines/wintermute/base/font/base_font_truetype.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_file_manager.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(BaseFont, false)
+
+//////////////////////////////////////////////////////////////////////
+BaseFont::BaseFont(BaseGame *inGame) : BaseObject(inGame) {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseFont::~BaseFont() {
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseFont::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) {
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int BaseFont::getTextHeight(byte *text, int width) {
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int BaseFont::getTextWidth(byte *text, int maxLength) {
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseFont::getLetterHeight() {
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFont::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseFont *BaseFont::createFromFile(BaseGame *gameRef, const Common::String &filename) {
+ if (isTrueType(gameRef, filename)) {
+ BaseFontTT *font = new BaseFontTT(gameRef);
+ if (font) {
+ if (DID_FAIL(font->loadFile(filename))) {
+ delete font;
+ return NULL;
+ }
+ }
+ return font;
+ } else {
+ BaseFontBitmap *font = new BaseFontBitmap(gameRef);
+ if (font) {
+ if (DID_FAIL(font->loadFile(filename))) {
+ delete font;
+ return NULL;
+ }
+ }
+ return font;
+ }
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(FONT)
+TOKEN_DEF(TTFONT)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool BaseFont::isTrueType(BaseGame *gameRef, const Common::String &filename) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(TTFONT)
+ TOKEN_TABLE_END
+
+
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ return false;
+ }
+
+ byte *workBuffer = buffer;
+
+ char *params;
+ BaseParser parser;
+
+ bool ret = false;
+ if (parser.getCommand((char **)&workBuffer, commands, (char **)&params) == TOKEN_TTFONT) {
+ ret = true;
+ }
+
+ delete[] buffer;
+ return ret;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/font/base_font.h b/engines/wintermute/base/font/base_font.h
new file mode 100644
index 0000000000..0abe62ab98
--- /dev/null
+++ b/engines/wintermute/base/font/base_font.h
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FONT_H
+#define WINTERMUTE_BASE_FONT_H
+
+#include "engines/wintermute/base/base_object.h"
+
+#define NUM_CHARACTERS 256
+
+namespace Wintermute {
+
+class BaseFont: public BaseObject {
+public:
+ DECLARE_PERSISTENT(BaseFont, BaseObject)
+ virtual int getTextWidth(byte *text, int maxLength = -1);
+ virtual int getTextHeight(byte *text, int width);
+ virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1);
+ virtual int getLetterHeight();
+
+ virtual void initLoop() {}
+ virtual void afterLoad() {}
+ BaseFont(BaseGame *inGame);
+ virtual ~BaseFont();
+
+ static BaseFont *createFromFile(BaseGame *game, const Common::String &filename);
+
+private:
+ //bool loadBuffer(byte * Buffer);
+ //bool loadFile(const char* Filename);
+ static bool isTrueType(BaseGame *game, const Common::String &filename);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/font/base_font_bitmap.cpp b/engines/wintermute/base/font/base_font_bitmap.cpp
new file mode 100644
index 0000000000..55f46c476b
--- /dev/null
+++ b/engines/wintermute/base/font/base_font_bitmap.cpp
@@ -0,0 +1,590 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/font/base_font_bitmap.h"
+#include "engines/wintermute/utils/string_util.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_frame.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/base_frame.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/platform_osystem.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(BaseFontBitmap, false)
+
+//////////////////////////////////////////////////////////////////////
+BaseFontBitmap::BaseFontBitmap(BaseGame *inGame) : BaseFont(inGame) {
+ _subframe = NULL;
+ _sprite = NULL;
+ _widthsFrame = 0;
+ memset(_widths, 0, NUM_CHARACTERS);
+ _tileWidth = _tileHeight = _numColumns = 0;
+ _fontextFix = false;
+ _freezable = false;
+ _wholeCell = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseFontBitmap::~BaseFontBitmap() {
+ delete _subframe;
+ delete _sprite;
+ _subframe = NULL;
+ _sprite = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseFontBitmap::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) {
+ textHeightDraw(text, x, y, width, align, true, maxHeight, maxLength);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int BaseFontBitmap::getTextHeight(byte *text, int width) {
+ return textHeightDraw(text, 0, 0, width, TAL_LEFT, false);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int BaseFontBitmap::getTextWidth(byte *text, int maxLength) {
+ AnsiString str;
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ WideString wstr = StringUtil::utf8ToWide(Utf8String((char *)text));
+ str = StringUtil::wideToAnsi(wstr);
+ } else {
+ str = AnsiString((char *)text);
+ }
+
+ if (maxLength >= 0 && str.size() > (uint32)maxLength) {
+ str = Common::String(str.c_str(), (uint32)maxLength);
+ }
+ //str.substr(0, maxLength); // TODO: Remove
+
+ int textWidth = 0;
+ for (int i = 0; (uint32)i < str.size(); i++) {
+ textWidth += getCharWidth((byte)str[i]);
+ }
+
+ return textWidth;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+int BaseFontBitmap::textHeightDraw(const byte *text, int x, int y, int width, TTextAlign align, bool draw, int maxHeight, int maxLength) {
+ if (maxLength == 0) {
+ return 0;
+ }
+
+ if (text == NULL || text[0] == '\0') {
+ return _tileHeight;
+ }
+
+ AnsiString str;
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ WideString wstr = StringUtil::utf8ToWide(Utf8String((const char *)text));
+ str = StringUtil::wideToAnsi(wstr);
+ } else {
+ str = AnsiString((const char *)text);
+ }
+ if (str.empty()) {
+ return 0;
+ }
+
+ int lineLength = 0;
+ int realLength = 0;
+ int numLines = 0;
+
+ int i;
+
+ int index = -1;
+ int start = 0;
+ int end = 0;
+ int last_end = 0;
+
+ bool done = false;
+ bool newLine = false;
+ bool longLine = false;
+
+ if (draw) {
+ _gameRef->_renderer->startSpriteBatch();
+ }
+
+ while (!done) {
+ if (maxHeight > 0 && (numLines + 1)*_tileHeight > maxHeight) {
+ if (draw) {
+ _gameRef->_renderer->endSpriteBatch();
+ }
+ return numLines * _tileHeight;
+ }
+
+ index++;
+
+ if (str[index] == ' ' && (maxHeight < 0 || maxHeight / _tileHeight > 1)) {
+ end = index - 1;
+ realLength = lineLength;
+ }
+
+ if (str[index] == '\n') {
+ end = index - 1;
+ realLength = lineLength;
+ newLine = true;
+ }
+
+ if (lineLength + getCharWidth(str[index]) > width && last_end == end) {
+ end = index - 1;
+ realLength = lineLength;
+ newLine = true;
+ longLine = true;
+ }
+
+ if ((int)str.size() == (index + 1) || (maxLength >= 0 && index == maxLength - 1)) {
+ done = true;
+ if (!newLine) {
+ end = index;
+ lineLength += getCharWidth(str[index]);
+ realLength = lineLength;
+ }
+ } else {
+ lineLength += getCharWidth(str[index]);
+ }
+
+ if ((lineLength > width) || done || newLine) {
+ if (end < 0) {
+ done = true;
+ }
+ int startX;
+ switch (align) {
+ case TAL_CENTER:
+ startX = x + (width - realLength) / 2;
+ break;
+ case TAL_RIGHT:
+ startX = x + width - realLength;
+ break;
+ case TAL_LEFT:
+ startX = x;
+ break;
+ default:
+ error("BaseFontBitmap::TextHeightDraw - Unhandled enum");
+ break;
+ }
+ for (i = start; i < end + 1; i++) {
+ if (draw) {
+ drawChar(str[i], startX, y);
+ }
+ startX += getCharWidth(str[i]);
+ }
+ y += _tileHeight;
+ last_end = end;
+ if (longLine) {
+ end--;
+ }
+ start = end + 2;
+ index = end + 1;
+ lineLength = 0;
+ newLine = false;
+ longLine = false;
+ numLines++;
+ }
+ }
+
+ if (draw) {
+ _gameRef->_renderer->endSpriteBatch();
+ }
+
+ return numLines * _tileHeight;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseFontBitmap::drawChar(byte c, int x, int y) {
+ if (_fontextFix) {
+ c--;
+ }
+
+ int row, col;
+
+ row = c / _numColumns;
+ col = c % _numColumns;
+
+ Rect32 rect;
+ /* l t r b */
+ int tileWidth;
+ if (_wholeCell) {
+ tileWidth = _tileWidth;
+ } else {
+ tileWidth = _widths[c];
+ }
+
+ BasePlatform::setRect(&rect, col * _tileWidth, row * _tileHeight, col * _tileWidth + tileWidth, (row + 1)*_tileHeight);
+ bool handled = false;
+ if (_sprite) {
+ _sprite->getCurrentFrame();
+ if (_sprite->_currentFrame >= 0 && _sprite->_currentFrame < (int32)_sprite->_frames.size() && _sprite->_frames[_sprite->_currentFrame]) {
+ if (_sprite->_frames[_sprite->_currentFrame]->_subframes.size() > 0) {
+ _sprite->_frames[_sprite->_currentFrame]->_subframes[0]->_surface->displayTrans(x, y, rect);
+ }
+ handled = true;
+ }
+ }
+ if (!handled && _subframe) {
+ _subframe->_surface->displayTrans(x, y, rect);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseFontBitmap::loadFile(const Common::String &filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "BaseFontBitmap::LoadFile failed for file '%s'", filename.c_str());
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename.c_str());
+
+ if (DID_FAIL(ret = loadBuffer(buffer))) {
+ _gameRef->LOG(0, "Error parsing FONT file '%s'", filename.c_str());
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(FONTEXT_FIX)
+TOKEN_DEF(FONT)
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(TRANSPARENT)
+TOKEN_DEF(COLUMNS)
+TOKEN_DEF(TILE_WIDTH)
+TOKEN_DEF(TILE_HEIGHT)
+TOKEN_DEF(DEFAULT_WIDTH)
+TOKEN_DEF(WIDTHS)
+TOKEN_DEF(AUTO_WIDTH)
+TOKEN_DEF(SPACE_WIDTH)
+TOKEN_DEF(EXPAND_WIDTH)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(SPRITE)
+TOKEN_DEF(WIDTHS_FRAME)
+TOKEN_DEF(PAINT_WHOLE_CELL)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////
+bool BaseFontBitmap::loadBuffer(byte *buffer) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(FONTEXT_FIX)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(TRANSPARENT)
+ TOKEN_TABLE(COLUMNS)
+ TOKEN_TABLE(TILE_WIDTH)
+ TOKEN_TABLE(TILE_HEIGHT)
+ TOKEN_TABLE(DEFAULT_WIDTH)
+ TOKEN_TABLE(WIDTHS)
+ TOKEN_TABLE(AUTO_WIDTH)
+ TOKEN_TABLE(SPACE_WIDTH)
+ TOKEN_TABLE(EXPAND_WIDTH)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(SPRITE)
+ TOKEN_TABLE(WIDTHS_FRAME)
+ TOKEN_TABLE(PAINT_WHOLE_CELL)
+ TOKEN_TABLE_END
+
+ char *params;
+ int cmd;
+ BaseParser parser;
+
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_FONT) {
+ _gameRef->LOG(0, "'FONT' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = (byte *)params;
+
+ int widths[300];
+ int num = 0, defaultWidth = 8;
+ int lastWidth = 0;
+ int i;
+ int r = 255, g = 255, b = 255;
+ bool custoTrans = false;
+ char *surfaceFile = NULL;
+ char *spriteFile = NULL;
+
+ bool autoWidth = false;
+ int spaceWidth = 0;
+ int expandWidth = 0;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+
+ switch (cmd) {
+ case TOKEN_IMAGE:
+ surfaceFile = (char *)params;
+ break;
+
+ case TOKEN_SPRITE:
+ spriteFile = (char *)params;
+ break;
+
+ case TOKEN_TRANSPARENT:
+ parser.scanStr(params, "%d,%d,%d", &r, &g, &b);
+ custoTrans = true;
+ break;
+
+ case TOKEN_WIDTHS:
+ parser.scanStr(params, "%D", widths, &num);
+ for (i = 0; lastWidth < NUM_CHARACTERS && num > 0; lastWidth++, num--, i++) {
+ _widths[lastWidth] = (byte)widths[i];
+ }
+ break;
+
+ case TOKEN_DEFAULT_WIDTH:
+ parser.scanStr(params, "%d", &defaultWidth);
+ break;
+
+ case TOKEN_WIDTHS_FRAME:
+ parser.scanStr(params, "%d", &_widthsFrame);
+ break;
+
+ case TOKEN_COLUMNS:
+ parser.scanStr(params, "%d", &_numColumns);
+ break;
+
+ case TOKEN_TILE_WIDTH:
+ parser.scanStr(params, "%d", &_tileWidth);
+ break;
+
+ case TOKEN_TILE_HEIGHT:
+ parser.scanStr(params, "%d", &_tileHeight);
+ break;
+
+ case TOKEN_AUTO_WIDTH:
+ parser.scanStr(params, "%b", &autoWidth);
+ break;
+
+ case TOKEN_FONTEXT_FIX:
+ parser.scanStr(params, "%b", &_fontextFix);
+ break;
+
+ case TOKEN_PAINT_WHOLE_CELL:
+ parser.scanStr(params, "%b", &_wholeCell);
+ break;
+
+ case TOKEN_SPACE_WIDTH:
+ parser.scanStr(params, "%d", &spaceWidth);
+ break;
+
+ case TOKEN_EXPAND_WIDTH:
+ parser.scanStr(params, "%d", &expandWidth);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty((byte *)params, false);
+ break;
+ }
+
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in FONT definition");
+ return STATUS_FAILED;
+ }
+
+ if (spriteFile != NULL) {
+ delete _sprite;
+ _sprite = new BaseSprite(_gameRef, this);
+ if (!_sprite || DID_FAIL(_sprite->loadFile(spriteFile))) {
+ delete _sprite;
+ _sprite = NULL;
+ }
+ }
+
+ if (surfaceFile != NULL && !_sprite) {
+ _subframe = new BaseSubFrame(_gameRef);
+ if (custoTrans) {
+ _subframe->setSurface(surfaceFile, false, r, g, b);
+ } else {
+ _subframe->setSurface(surfaceFile);
+ }
+ }
+
+
+ if (((_subframe == NULL || _subframe->_surface == NULL) && _sprite == NULL) || _numColumns == 0 || _tileWidth == 0 || _tileHeight == 0) {
+ _gameRef->LOG(0, "Incomplete font definition");
+ return STATUS_FAILED;
+ }
+
+ if (autoWidth) {
+ // calculate characters width
+ getWidths();
+
+ // do we need to modify widths?
+ if (expandWidth != 0) {
+ for (i = 0; i < NUM_CHARACTERS; i++) {
+ int newWidth = (int)_widths[i] + expandWidth;
+ if (newWidth < 0) {
+ newWidth = 0;
+ }
+
+ _widths[i] = (byte)newWidth;
+ }
+ }
+
+ // handle space character
+ uint32 spaceChar = ' ';
+ if (_fontextFix) {
+ spaceChar--;
+ }
+
+ if (spaceWidth != 0) {
+ _widths[spaceChar] = spaceWidth;
+ } else {
+ if (_widths[spaceChar] == expandWidth || _widths[spaceChar] == 0) {
+ _widths[spaceChar] = (_widths['m'] + _widths['i']) / 2;
+ }
+ }
+ } else {
+ for (i = lastWidth; i < NUM_CHARACTERS; i++) {
+ _widths[i] = defaultWidth;
+ }
+ }
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontBitmap::persist(BasePersistenceManager *persistMgr) {
+
+ BaseFont::persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_numColumns));
+
+ persistMgr->transfer(TMEMBER(_subframe));
+ persistMgr->transfer(TMEMBER(_tileHeight));
+ persistMgr->transfer(TMEMBER(_tileWidth));
+ persistMgr->transfer(TMEMBER(_sprite));
+ persistMgr->transfer(TMEMBER(_widthsFrame));
+
+ if (persistMgr->getIsSaving()) {
+ persistMgr->putBytes(_widths, sizeof(_widths));
+ } else {
+ persistMgr->getBytes(_widths, sizeof(_widths));
+ }
+
+
+ persistMgr->transfer(TMEMBER(_fontextFix));
+ persistMgr->transfer(TMEMBER(_wholeCell));
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseFontBitmap::getCharWidth(byte index) {
+ if (_fontextFix) {
+ index--;
+ }
+ return _widths[index];
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontBitmap::getWidths() {
+ BaseSurface *surf = NULL;
+
+ if (_sprite) {
+ if (_widthsFrame >= 0 && _widthsFrame < (int32)_sprite->_frames.size()) {
+ if (_sprite->_frames[_widthsFrame] && (int32)_sprite->_frames[_widthsFrame]->_subframes.size() > 0) {
+ surf = _sprite->_frames[_widthsFrame]->_subframes[0]->_surface;
+ }
+ }
+ }
+ if (surf == NULL && _subframe) {
+ surf = _subframe->_surface;
+ }
+ if (!surf || DID_FAIL(surf->startPixelOp())) {
+ return STATUS_FAILED;
+ }
+
+
+ for (int i = 0; i < NUM_CHARACTERS; i++) {
+ int xxx = (i % _numColumns) * _tileWidth;
+ int yyy = (i / _numColumns) * _tileHeight;
+
+
+ int minCol = -1;
+ for (int row = 0; row < _tileHeight; row++) {
+ for (int col = _tileWidth - 1; col >= minCol + 1; col--) {
+ if (xxx + col < 0 || xxx + col >= surf->getWidth() || yyy + row < 0 || yyy + row >= surf->getHeight()) {
+ continue;
+ }
+ if (!surf->isTransparentAtLite(xxx + col, yyy + row)) {
+ //min_col = col;
+ minCol = MAX(col, minCol);
+ break;
+ }
+ }
+ if (minCol == _tileWidth - 1) {
+ break;
+ }
+ }
+
+ _widths[i] = minCol + 1;
+ }
+ surf->endPixelOp();
+ /*
+ _gameRef->LOG(0, "----- %s ------", _filename);
+ for(int j=0; j<16; j++)
+ {
+ _gameRef->LOG(0, "%02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d", _widths[j*16+0], _widths[j*16+1], _widths[j*16+2], _widths[j*16+3], _widths[j*16+4], _widths[j*16+5], _widths[j*16+6], _widths[j*16+7], _widths[j*16+8], _widths[j*16+9], _widths[j*16+10], _widths[j*16+11], _widths[j*16+12], _widths[j*16+13], _widths[j*16+14], _widths[j*16+15]);
+ }
+ */
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int BaseFontBitmap::getLetterHeight() {
+ return _tileHeight;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/font/base_font_bitmap.h b/engines/wintermute/base/font/base_font_bitmap.h
new file mode 100644
index 0000000000..2f3a69d097
--- /dev/null
+++ b/engines/wintermute/base/font/base_font_bitmap.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FONTBITMAP_H
+#define WINTERMUTE_BASE_FONTBITMAP_H
+
+
+#include "engines/wintermute/base/font/base_font.h"
+
+namespace Wintermute {
+class BaseSubFrame;
+class BaseFontBitmap : public BaseFont {
+public:
+ DECLARE_PERSISTENT(BaseFontBitmap, BaseFont)
+ bool loadBuffer(byte *Buffer);
+ bool loadFile(const Common::String &filename);
+ virtual int getTextWidth(byte *text, int maxLength = -1);
+ virtual int getTextHeight(byte *text, int width);
+ virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1);
+ virtual int getLetterHeight();
+
+ BaseFontBitmap(BaseGame *inGame);
+ virtual ~BaseFontBitmap();
+
+private:
+ bool getWidths();
+ BaseSprite *_sprite;
+ int _widthsFrame;
+ bool _fontextFix;
+ int _numColumns;
+ int _tileHeight;
+ int _tileWidth;
+ byte _widths[NUM_CHARACTERS];
+ BaseSubFrame *_subframe;
+ bool _wholeCell;
+
+ int getCharWidth(byte index);
+ void drawChar(byte c, int x, int y);
+
+ int textHeightDraw(const byte *text, int x, int y, int width, TTextAlign align, bool draw, int max_height = -1, int maxLength = -1);
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/font/base_font_storage.cpp b/engines/wintermute/base/font/base_font_storage.cpp
new file mode 100644
index 0000000000..8128ffe897
--- /dev/null
+++ b/engines/wintermute/base/font/base_font_storage.cpp
@@ -0,0 +1,141 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/base_game.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(BaseFontStorage, true)
+
+//////////////////////////////////////////////////////////////////////////
+BaseFontStorage::BaseFontStorage(BaseGame *inGame) : BaseClass(inGame) {
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseFontStorage::~BaseFontStorage() {
+ cleanup(true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontStorage::cleanup(bool warn) {
+ for (uint32 i = 0; i < _fonts.size(); i++) {
+ if (warn) {
+ _gameRef->LOG(0, "Removing orphan font '%s'", _fonts[i]->getFilename());
+ }
+ delete _fonts[i];
+ }
+ _fonts.clear();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontStorage::initLoop() {
+ for (uint32 i = 0; i < _fonts.size(); i++) {
+ _fonts[i]->initLoop();
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseFont *BaseFontStorage::addFont(const Common::String &filename) {
+ if (!filename.size()) {
+ return NULL;
+ }
+
+ for (uint32 i = 0; i < _fonts.size(); i++) {
+ if (scumm_stricmp(_fonts[i]->getFilename(), filename.c_str()) == 0) {
+ _fonts[i]->_refCount++;
+ return _fonts[i];
+ }
+ }
+
+ /*
+ BaseFont* font = new BaseFont(_gameRef);
+ if (!font) return NULL;
+
+ if (DID_FAIL(font->loadFile(filename))) {
+ delete font;
+ return NULL;
+ }
+ else {
+ font->_refCount = 1;
+ _fonts.add(font);
+ return font;
+ }
+ */
+ BaseFont *font = BaseFont::createFromFile(_gameRef, filename);
+ if (font) {
+ font->_refCount = 1;
+ _fonts.add(font);
+ }
+ return font;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontStorage::removeFont(BaseFont *font) {
+ if (!font) {
+ return STATUS_FAILED;
+ }
+
+ for (uint32 i = 0; i < _fonts.size(); i++) {
+ if (_fonts[i] == font) {
+ _fonts[i]->_refCount--;
+ if (_fonts[i]->_refCount <= 0) {
+ delete _fonts[i];
+ _fonts.remove_at(i);
+ }
+ break;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontStorage::persist(BasePersistenceManager *persistMgr) {
+
+ if (!persistMgr->getIsSaving()) {
+ cleanup(false);
+ }
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+ _fonts.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/font/base_font_storage.h b/engines/wintermute/base/font/base_font_storage.h
new file mode 100644
index 0000000000..60874167e7
--- /dev/null
+++ b/engines/wintermute/base/font/base_font_storage.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FONTSTORAGE_H
+#define WINTERMUTE_BASE_FONTSTORAGE_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/coll_templ.h"
+
+namespace Wintermute {
+
+class BaseFont;
+
+class BaseFontStorage : public BaseClass {
+public:
+ DECLARE_PERSISTENT(BaseFontStorage, BaseClass)
+ bool cleanup(bool warn = false);
+ bool removeFont(BaseFont *font);
+ BaseFont *addFont(const Common::String &filename);
+ BaseFontStorage(BaseGame *inGame);
+ virtual ~BaseFontStorage();
+ BaseArray<BaseFont *> _fonts;
+ bool initLoop();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/font/base_font_truetype.cpp b/engines/wintermute/base/font/base_font_truetype.cpp
new file mode 100644
index 0000000000..3219918e6d
--- /dev/null
+++ b/engines/wintermute/base/font/base_font_truetype.cpp
@@ -0,0 +1,602 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/font/base_font_truetype.h"
+#include "engines/wintermute/utils/string_util.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/wintermute.h"
+#include "graphics/fonts/ttf.h"
+#include "graphics/fontman.h"
+#include <limits.h>
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseFontTT, false)
+
+//////////////////////////////////////////////////////////////////////////
+BaseFontTT::BaseFontTT(BaseGame *inGame) : BaseFont(inGame) {
+ _fontHeight = 12;
+ _isBold = _isItalic = _isUnderline = _isStriked = false;
+
+ _fontFile = NULL;
+ _font = NULL;
+ _fallbackFont = NULL;
+ _deletableFont = NULL;
+
+ for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
+ _cachedTexts[i] = NULL;
+ }
+
+ _lineHeight = 0;
+ _maxCharWidth = _maxCharHeight = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseFontTT::~BaseFontTT(void) {
+ clearCache();
+
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ delete _layers[i];
+ }
+ _layers.clear();
+
+ delete[] _fontFile;
+ _fontFile = NULL;
+
+ delete _deletableFont;
+ _font = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseFontTT::clearCache() {
+ for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
+ if (_cachedTexts[i]) {
+ delete _cachedTexts[i];
+ }
+ _cachedTexts[i] = NULL;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseFontTT::initLoop() {
+ // we need more aggressive cache management on iOS not to waste too much memory on fonts
+ if (_gameRef->_constrainedMemory) {
+ // purge all cached images not used in the last frame
+ for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
+ if (_cachedTexts[i] == NULL) {
+ continue;
+ }
+
+ if (!_cachedTexts[i]->_marked) {
+ delete _cachedTexts[i];
+ _cachedTexts[i] = NULL;
+ } else {
+ _cachedTexts[i]->_marked = false;
+ }
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+int BaseFontTT::getTextWidth(byte *text, int maxLength) {
+ WideString textStr;
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ textStr = StringUtil::utf8ToWide((char *)text);
+ } else {
+ textStr = StringUtil::ansiToWide((char *)text);
+ }
+
+ if (maxLength >= 0 && textStr.size() > (uint32)maxLength) {
+ textStr = Common::String(textStr.c_str(), (uint32)maxLength);
+ }
+ //text = text.substr(0, MaxLength); // TODO: Remove
+
+ int textWidth, textHeight;
+ measureText(textStr, -1, -1, textWidth, textHeight);
+
+ return textWidth;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int BaseFontTT::getTextHeight(byte *text, int width) {
+ WideString textStr;
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ textStr = StringUtil::utf8ToWide((char *)text);
+ } else {
+ textStr = StringUtil::ansiToWide((char *)text);
+ }
+
+
+ int textWidth, textHeight;
+ measureText(textStr, width, -1, textWidth, textHeight);
+
+ return textHeight;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseFontTT::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) {
+ if (text == NULL || strcmp((const char *)text, "") == 0) {
+ return;
+ }
+
+ WideString textStr = (const char *)text;
+
+ // TODO: Why do we still insist on Widestrings everywhere?
+ /* if (_gameRef->_textEncoding == TEXT_UTF8) text = StringUtil::Utf8ToWide((char *)Text);
+ else text = StringUtil::AnsiToWide((char *)Text);*/
+
+ if (maxLength >= 0 && textStr.size() > (uint32)maxLength) {
+ textStr = Common::String(textStr.c_str(), (uint32)maxLength);
+ }
+ //text = text.substr(0, MaxLength); // TODO: Remove
+
+ BaseRenderer *renderer = _gameRef->_renderer;
+
+ // find cached surface, if exists
+ int minPriority = INT_MAX;
+ int minIndex = -1;
+ BaseSurface *surface = NULL;
+ int textOffset = 0;
+
+ for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
+ if (_cachedTexts[i] == NULL) {
+ minPriority = 0;
+ minIndex = i;
+ } else {
+ if (_cachedTexts[i]->_text == textStr && _cachedTexts[i]->_align == align && _cachedTexts[i]->_width == width && _cachedTexts[i]->_maxHeight == maxHeight && _cachedTexts[i]->_maxLength == maxLength) {
+ surface = _cachedTexts[i]->_surface;
+ textOffset = _cachedTexts[i]->_textOffset;
+ _cachedTexts[i]->_priority++;
+ _cachedTexts[i]->_marked = true;
+ break;
+ } else {
+ if (_cachedTexts[i]->_priority < minPriority) {
+ minPriority = _cachedTexts[i]->_priority;
+ minIndex = i;
+ }
+ }
+ }
+ }
+
+ // not found, create one
+ if (!surface) {
+ debugC(kWintermuteDebugFont, "Draw text: %s", text);
+ surface = renderTextToTexture(textStr, width, align, maxHeight, textOffset);
+ if (surface) {
+ // write surface to cache
+ if (_cachedTexts[minIndex] != NULL) {
+ delete _cachedTexts[minIndex];
+ }
+ _cachedTexts[minIndex] = new BaseCachedTTFontText;
+
+ _cachedTexts[minIndex]->_surface = surface;
+ _cachedTexts[minIndex]->_align = align;
+ _cachedTexts[minIndex]->_width = width;
+ _cachedTexts[minIndex]->_maxHeight = maxHeight;
+ _cachedTexts[minIndex]->_maxLength = maxLength;
+ _cachedTexts[minIndex]->_priority = 1;
+ _cachedTexts[minIndex]->_text = textStr;
+ _cachedTexts[minIndex]->_textOffset = textOffset;
+ _cachedTexts[minIndex]->_marked = true;
+ }
+ }
+
+
+ // and paint it
+ if (surface) {
+ Rect32 rc;
+ BasePlatform::setRect(&rc, 0, 0, surface->getWidth(), surface->getHeight());
+ for (uint32 i = 0; i < _layers.size(); i++) {
+ uint32 color = _layers[i]->_color;
+ uint32 origForceAlpha = renderer->_forceAlphaColor;
+ if (renderer->_forceAlphaColor != 0) {
+ color = BYTETORGBA(RGBCOLGetR(color), RGBCOLGetG(color), RGBCOLGetB(color), RGBCOLGetA(renderer->_forceAlphaColor));
+ renderer->_forceAlphaColor = 0;
+ }
+ surface->displayTransOffset(x, y - textOffset, rc, color, BLEND_NORMAL, false, false, _layers[i]->_offsetX, _layers[i]->_offsetY);
+
+ renderer->_forceAlphaColor = origForceAlpha;
+ }
+ }
+
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset) {
+ //TextLineList lines;
+ // TODO: Use WideString-conversion here.
+ //WrapText(text, width, maxHeight, lines);
+ Common::Array<Common::String> lines;
+ _font->wordWrapText(text, width, lines);
+
+ while (maxHeight > 0 && lines.size() * _lineHeight > maxHeight) {
+ lines.pop_back();
+ }
+ if (lines.size() == 0) {
+ return NULL;
+ }
+
+ Graphics::TextAlign alignment = Graphics::kTextAlignInvalid;
+ if (align == TAL_LEFT) {
+ alignment = Graphics::kTextAlignLeft;
+ } else if (align == TAL_CENTER) {
+ alignment = Graphics::kTextAlignCenter;
+ } else if (align == TAL_RIGHT) {
+ alignment = Graphics::kTextAlignRight;
+ }
+
+ debugC(kWintermuteDebugFont, "%s %d %d %d %d", text.c_str(), RGBCOLGetR(_layers[0]->_color), RGBCOLGetG(_layers[0]->_color), RGBCOLGetB(_layers[0]->_color), RGBCOLGetA(_layers[0]->_color));
+// void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const;
+ Graphics::Surface *surface = new Graphics::Surface();
+ if (_deletableFont) { // We actually have a TTF
+ surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
+ } else { // We are using a fallback, they can't do 32bpp
+ surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0));
+ }
+ uint32 useColor = 0xffffffff;
+ Common::Array<Common::String>::iterator it;
+ int heightOffset = 0;
+ for (it = lines.begin(); it != lines.end(); ++it) {
+ _font->drawString(surface, *it, 0, heightOffset, width, useColor, alignment);
+ heightOffset += (int)_lineHeight;
+ }
+
+ BaseSurface *retSurface = _gameRef->_renderer->createSurface();
+ Graphics::Surface *convertedSurface = surface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
+ retSurface->putSurface(*convertedSurface, true);
+ convertedSurface->free();
+ surface->free();
+ delete surface;
+ delete convertedSurface;
+ return retSurface;
+ // TODO: _isUnderline, _isBold, _isItalic, _isStriked
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int BaseFontTT::getLetterHeight() {
+ return (int)getLineHeight();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseFontTT::loadFile(const Common::String &filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "BaseFontTT::LoadFile failed for file '%s'", filename.c_str());
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename.c_str());
+
+ if (DID_FAIL(ret = loadBuffer(buffer))) {
+ _gameRef->LOG(0, "Error parsing TTFONT file '%s'", filename.c_str());
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(TTFONT)
+TOKEN_DEF(SIZE)
+TOKEN_DEF(FACE)
+TOKEN_DEF(FILENAME)
+TOKEN_DEF(BOLD)
+TOKEN_DEF(ITALIC)
+TOKEN_DEF(UNDERLINE)
+TOKEN_DEF(STRIKE)
+TOKEN_DEF(CHARSET)
+TOKEN_DEF(COLOR)
+TOKEN_DEF(ALPHA)
+TOKEN_DEF(LAYER)
+TOKEN_DEF(OFFSET_X)
+TOKEN_DEF(OFFSET_Y)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////
+bool BaseFontTT::loadBuffer(byte *buffer) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(TTFONT)
+ TOKEN_TABLE(SIZE)
+ TOKEN_TABLE(FACE)
+ TOKEN_TABLE(FILENAME)
+ TOKEN_TABLE(BOLD)
+ TOKEN_TABLE(ITALIC)
+ TOKEN_TABLE(UNDERLINE)
+ TOKEN_TABLE(STRIKE)
+ TOKEN_TABLE(CHARSET)
+ TOKEN_TABLE(COLOR)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE(LAYER)
+ TOKEN_TABLE_END
+
+ char *params;
+ int cmd;
+ BaseParser parser;
+
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_TTFONT) {
+ _gameRef->LOG(0, "'TTFONT' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = (byte *)params;
+
+ uint32 baseColor = 0x00000000;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_SIZE:
+ parser.scanStr(params, "%d", &_fontHeight);
+ break;
+
+ case TOKEN_FACE:
+ // we don't need this anymore
+ break;
+
+ case TOKEN_FILENAME:
+ BaseUtils::setString(&_fontFile, params);
+ break;
+
+ case TOKEN_BOLD:
+ parser.scanStr(params, "%b", &_isBold);
+ break;
+
+ case TOKEN_ITALIC:
+ parser.scanStr(params, "%b", &_isItalic);
+ break;
+
+ case TOKEN_UNDERLINE:
+ parser.scanStr(params, "%b", &_isUnderline);
+ break;
+
+ case TOKEN_STRIKE:
+ parser.scanStr(params, "%b", &_isStriked);
+ break;
+
+ case TOKEN_CHARSET:
+ // we don't need this anymore
+ break;
+
+ case TOKEN_COLOR: {
+ int r, g, b;
+ parser.scanStr(params, "%d,%d,%d", &r, &g, &b);
+ baseColor = BYTETORGBA(r, g, b, RGBCOLGetA(baseColor));
+ }
+ break;
+
+ case TOKEN_ALPHA: {
+ int a;
+ parser.scanStr(params, "%d", &a);
+ baseColor = BYTETORGBA(RGBCOLGetR(baseColor), RGBCOLGetG(baseColor), RGBCOLGetB(baseColor), a);
+ }
+ break;
+
+ case TOKEN_LAYER: {
+ BaseTTFontLayer *layer = new BaseTTFontLayer;
+ if (layer && DID_SUCCEED(parseLayer(layer, (byte *)params))) {
+ _layers.add(layer);
+ } else {
+ delete layer;
+ layer = NULL;
+ cmd = PARSERR_TOKENNOTFOUND;
+ }
+ }
+ break;
+
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in TTFONT definition");
+ return STATUS_FAILED;
+ }
+
+ // create at least one layer
+ if (_layers.size() == 0) {
+ BaseTTFontLayer *layer = new BaseTTFontLayer;
+ layer->_color = baseColor;
+ _layers.add(layer);
+ }
+
+ if (!_fontFile) {
+ BaseUtils::setString(&_fontFile, "arial.ttf");
+ }
+
+ return initFont();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontTT::parseLayer(BaseTTFontLayer *layer, byte *buffer) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(OFFSET_X)
+ TOKEN_TABLE(OFFSET_Y)
+ TOKEN_TABLE(COLOR)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE_END
+
+ char *params;
+ int cmd;
+ BaseParser parser;
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_OFFSET_X:
+ parser.scanStr(params, "%d", &layer->_offsetX);
+ break;
+
+ case TOKEN_OFFSET_Y:
+ parser.scanStr(params, "%d", &layer->_offsetY);
+ break;
+
+ case TOKEN_COLOR: {
+ int r, g, b;
+ parser.scanStr(params, "%d,%d,%d", &r, &g, &b);
+ layer->_color = BYTETORGBA(r, g, b, RGBCOLGetA(layer->_color));
+ }
+ break;
+
+ case TOKEN_ALPHA: {
+ int a;
+ parser.scanStr(params, "%d", &a);
+ layer->_color = BYTETORGBA(RGBCOLGetR(layer->_color), RGBCOLGetG(layer->_color), RGBCOLGetB(layer->_color), a);
+ }
+ break;
+ }
+ }
+ if (cmd != PARSERR_EOF) {
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontTT::persist(BasePersistenceManager *persistMgr) {
+ BaseFont::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_isBold));
+ persistMgr->transfer(TMEMBER(_isItalic));
+ persistMgr->transfer(TMEMBER(_isUnderline));
+ persistMgr->transfer(TMEMBER(_isStriked));
+ persistMgr->transfer(TMEMBER(_fontHeight));
+ persistMgr->transfer(TMEMBER(_fontFile));
+
+
+ // persist layers
+ int numLayers;
+ if (persistMgr->getIsSaving()) {
+ numLayers = _layers.size();
+ persistMgr->transfer(TMEMBER(numLayers));
+ for (int i = 0; i < numLayers; i++) {
+ _layers[i]->persist(persistMgr);
+ }
+ } else {
+ numLayers = _layers.size();
+ persistMgr->transfer(TMEMBER(numLayers));
+ for (int i = 0; i < numLayers; i++) {
+ BaseTTFontLayer *layer = new BaseTTFontLayer;
+ layer->persist(persistMgr);
+ _layers.add(layer);
+ }
+ }
+
+ if (!persistMgr->getIsSaving()) {
+ for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
+ _cachedTexts[i] = NULL;
+ }
+ _fallbackFont = _font = _deletableFont = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseFontTT::afterLoad() {
+ initFont();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseFontTT::initFont() {
+ if (!_fontFile) {
+ return STATUS_FAILED;
+ }
+
+ Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(_fontFile);
+ if (!file) {
+ //TODO: Try to fallback from Arial to FreeSans
+ /*
+ // the requested font file is not in wme file space; try loading a system font
+ AnsiString fontFileName = PathUtil::combine(BasePlatform::getSystemFontPath(), PathUtil::getFileName(_fontFile));
+ file = BaseFileManager::getEngineInstance()->openFile(fontFileName.c_str(), false);
+ if (!file) {
+ _gameRef->LOG(0, "Error loading TrueType font '%s'", _fontFile);
+ //return STATUS_FAILED;
+ }*/
+ }
+
+ if (file) {
+#ifdef USE_FREETYPE2
+ _deletableFont = Graphics::loadTTFFont(*file, 96, _fontHeight); // Use the same dpi as WME (96 vs 72).
+ _font = _deletableFont;
+#endif
+ }
+ if (!_font) {
+ _font = _fallbackFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+ warning("BaseFontTT::InitFont - Couldn't load font: %s", _fontFile);
+ }
+ _lineHeight = _font->getFontHeight();
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseFontTT::measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight) {
+ //TextLineList lines;
+
+ if (maxWidth >= 0) {
+ Common::Array<Common::String> lines;
+ _font->wordWrapText(text, maxWidth, lines);
+ Common::Array<Common::String>::iterator it;
+ textWidth = 0;
+ for (it = lines.begin(); it != lines.end(); ++it) {
+ textWidth = MAX(textWidth, _font->getStringWidth(*it));
+ }
+
+ //WrapText(text, maxWidth, maxHeight, lines);
+
+ textHeight = (int)(lines.size() * getLineHeight());
+ } else {
+ textWidth = _font->getStringWidth(text);
+ textHeight = _fontHeight;
+ }
+ /*
+ TextLineList::iterator it;
+ for (it = lines.begin(); it != lines.end(); ++it) {
+ TextLine *line = (*it);
+ textWidth = MAX(textWidth, line->GetWidth());
+ delete line;
+ line = NULL;
+ }*/
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/font/base_font_truetype.h b/engines/wintermute/base/font/base_font_truetype.h
new file mode 100644
index 0000000000..02dca7439f
--- /dev/null
+++ b/engines/wintermute/base/font/base_font_truetype.h
@@ -0,0 +1,152 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_FONTTT_H
+#define WINTERMUTE_BASE_FONTTT_H
+
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+#include "graphics/font.h"
+
+#define NUM_CACHED_TEXTS 30
+
+namespace Wintermute {
+
+class BaseFontTT : public BaseFont {
+private:
+ //////////////////////////////////////////////////////////////////////////
+ class BaseCachedTTFontText {
+ public:
+ WideString _text;
+ int _width;
+ TTextAlign _align;
+ int _maxHeight;
+ int _maxLength;
+ BaseSurface *_surface;
+ int _priority;
+ int _textOffset;
+ bool _marked;
+
+ BaseCachedTTFontText() {
+ //_text = L"";
+ _text = "";
+ _width = _maxHeight = _maxLength = -1;
+ _align = TAL_LEFT;
+ _surface = NULL;
+ _priority = -1;
+ _textOffset = 0;
+ _marked = false;
+ }
+
+ virtual ~BaseCachedTTFontText() {
+ if (_surface) {
+ delete _surface;
+ }
+ }
+ };
+
+public:
+ //////////////////////////////////////////////////////////////////////////
+ class BaseTTFontLayer {
+ public:
+ BaseTTFontLayer() {
+ _offsetX = _offsetY = 0;
+ _color = 0x00000000;
+ }
+
+ bool persist(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_offsetX));
+ persistMgr->transfer(TMEMBER(_offsetY));
+ persistMgr->transfer(TMEMBER(_color));
+ return STATUS_OK;
+ }
+
+ int _offsetX;
+ int _offsetY;
+ uint32 _color;
+ };
+
+public:
+ DECLARE_PERSISTENT(BaseFontTT, BaseFont)
+ BaseFontTT(BaseGame *inGame);
+ virtual ~BaseFontTT(void);
+
+ virtual int getTextWidth(byte *text, int maxLength = -1);
+ virtual int getTextHeight(byte *text, int width);
+ virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1);
+ virtual int getLetterHeight();
+
+ bool loadBuffer(byte *buffer);
+ bool loadFile(const Common::String &filename);
+
+ float getLineHeight() const {
+ return _lineHeight;
+ }
+
+ void afterLoad();
+ void initLoop();
+
+private:
+ bool parseLayer(BaseTTFontLayer *layer, byte *buffer);
+
+ void measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight);
+
+ BaseSurface *renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset);
+
+ BaseCachedTTFontText *_cachedTexts[NUM_CACHED_TEXTS];
+
+ bool initFont();
+
+ Graphics::Font *_deletableFont;
+ const Graphics::Font *_font;
+ const Graphics::Font *_fallbackFont;
+
+ float _lineHeight;
+
+ size_t _maxCharWidth;
+ size_t _maxCharHeight;
+
+public:
+ bool _isBold;
+ bool _isItalic;
+ bool _isUnderline;
+ bool _isStriked;
+ int _fontHeight;
+ char *_fontFile;
+
+ BaseArray<BaseTTFontLayer *> _layers;
+ void clearCache();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/base_image.cpp b/engines/wintermute/base/gfx/base_image.cpp
new file mode 100644
index 0000000000..4b15d563ed
--- /dev/null
+++ b/engines/wintermute/base/gfx/base_image.cpp
@@ -0,0 +1,231 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/graphics/transparent_surface.h"
+#include "graphics/decoders/png.h"
+#include "graphics/decoders/jpeg.h"
+#include "graphics/decoders/bmp.h"
+#include "graphics/decoders/tga.h"
+#include "graphics/surface.h"
+#include "common/textconsole.h"
+#include "common/stream.h"
+#include "common/system.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+BaseImage::BaseImage() {
+ _fileManager = BaseFileManager::getEngineInstance();
+ _palette = NULL;
+ _surface = NULL;
+ _decoder = NULL;
+ _deletableSurface = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseImage::~BaseImage() {
+ delete _decoder;
+ if (_deletableSurface) {
+ _deletableSurface->free();
+ }
+ delete _deletableSurface;
+}
+
+bool BaseImage::loadFile(const Common::String &filename) {
+ _filename = filename;
+ _filename.toLowercase();
+ if (filename.hasPrefix("savegame:")) {
+ _decoder = new Graphics::BitmapDecoder();
+ } else if (_filename.hasSuffix(".png")) {
+ _decoder = new Graphics::PNGDecoder();
+ } else if (_filename.hasSuffix(".bmp")) {
+ _decoder = new Graphics::BitmapDecoder();
+ } else if (_filename.hasSuffix(".tga")) {
+ _decoder = new Graphics::TGADecoder();
+ } else if (_filename.hasSuffix(".jpg")) {
+ _decoder = new Graphics::JPEGDecoder();
+ } else {
+ error("BaseImage::loadFile : Unsupported fileformat %s", filename.c_str());
+ }
+ _filename = filename;
+ Common::SeekableReadStream *file = _fileManager->openFile(filename.c_str());
+ if (!file) {
+ return false;
+ }
+
+ _decoder->loadStream(*file);
+ _surface = _decoder->getSurface();
+ _palette = _decoder->getPalette();
+ _fileManager->closeFile(file);
+
+ return true;
+}
+
+byte BaseImage::getAlphaAt(int x, int y) const {
+ if (!_surface) {
+ return 0xFF;
+ }
+ uint32 color = *(const uint32 *)_surface->getBasePtr(x, y);
+ byte r, g, b, a;
+ _surface->format.colorToARGB(color, a, r, g, b);
+ return a;
+}
+
+void BaseImage::copyFrom(const Graphics::Surface *surface) {
+ _surface = _deletableSurface = new Graphics::Surface();
+ _deletableSurface->copyFrom(*surface);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseImage::saveBMPFile(const char *filename) const {
+ warning("BaseImage::saveBMPFile - stubbed"); // TODO
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseImage::resize(int newWidth, int newHeight) {
+ // WME Lite used FILTER_BILINEAR with FreeImage_Rescale here.
+ TransparentSurface temp(*_surface, true);
+ if (_deletableSurface) {
+ _deletableSurface->free();
+ delete _deletableSurface;
+ _deletableSurface = NULL;
+ }
+ _surface = _deletableSurface = temp.scale((uint16)newWidth, (uint16)newHeight);
+ temp.free();
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseImage::writeBMPToStream(Common::WriteStream *stream) const {
+ if (!_surface) {
+ return false;
+ }
+
+ /* The following is just copied over and inverted to write-ops from the BMP-decoder */
+ stream->writeByte('B');
+ stream->writeByte('M');
+
+ /* Since we don't care during reads, we don't care during writes: */
+ /* uint32 fileSize = */
+ stream->writeUint32LE(54 + _surface->h * _surface->pitch);
+ /* uint16 res1 = */
+ stream->writeUint16LE(0);
+ /* uint16 res2 = */
+ stream->writeUint16LE(0);
+ const uint32 imageOffset = 54;
+ stream->writeUint32LE(imageOffset);
+
+ const uint32 infoSize = 40; /* Windows v3 BMP */
+ stream->writeUint32LE(infoSize);
+
+ uint32 width = _surface->w;
+ int32 height = _surface->h;
+ stream->writeUint32LE(width);
+ stream->writeUint32LE((uint32)height);
+
+ if (width == 0 || height == 0) {
+ return false;
+ }
+
+ if (height < 0) {
+ warning("Right-side up bitmaps not supported");
+ return false;
+ }
+
+ /* uint16 planes = */ stream->writeUint16LE(1);
+ const uint16 bitsPerPixel = 24;
+ stream->writeUint16LE(bitsPerPixel);
+
+ const uint32 compression = 0;
+ stream->writeUint32LE(compression);
+
+ /* uint32 imageSize = */
+ stream->writeUint32LE(_surface->h * _surface->pitch);
+ /* uint32 pixelsPerMeterX = */
+ stream->writeUint32LE(0);
+ /* uint32 pixelsPerMeterY = */
+ stream->writeUint32LE(0);
+ const uint32 paletteColorCount = 0;
+ stream->writeUint32LE(paletteColorCount);
+ /* uint32 colorsImportant = */
+ stream->writeUint32LE(0);
+
+ // Start us at the beginning of the image (54 bytes in)
+ Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
+
+ // BGRA for 24bpp
+ if (bitsPerPixel == 24) {
+ format = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0);
+ }
+
+ Graphics::Surface *surface = _surface->convertTo(format);
+
+ int srcPitch = width * (bitsPerPixel >> 3);
+ const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
+
+ for (int32 i = height - 1; i >= 0; i--) {
+ for (uint32 j = 0; j < width; j++) {
+ byte b, g, r;
+ uint32 color = *(uint32 *)surface->getBasePtr(j, i);
+ surface->format.colorToRGB(color, r, g, b);
+ stream->writeByte(b);
+ stream->writeByte(g);
+ stream->writeByte(r);
+ }
+
+ for (int k = 0; k < extraDataLength; k++) {
+ stream->writeByte(0);
+ }
+ }
+ surface->free();
+ delete surface;
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseImage::copyFrom(BaseImage *origImage, int newWidth, int newHeight) {
+ // WME Lite used FILTER_BILINEAR with FreeImage_Rescale here.
+
+ TransparentSurface temp(*origImage->_surface, false);
+ if (_deletableSurface) {
+ _deletableSurface->free();
+ delete _deletableSurface;
+ _deletableSurface = NULL;
+ }
+ _surface = _deletableSurface = temp.scale((uint16)newWidth, (uint16)newHeight);
+ return true;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/gfx/base_image.h b/engines/wintermute/base/gfx/base_image.h
new file mode 100644
index 0000000000..6d01b84184
--- /dev/null
+++ b/engines/wintermute/base/gfx/base_image.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_IMAGE_H
+#define WINTERMUTE_BASE_IMAGE_H
+
+#include "graphics/surface.h"
+#include "graphics/pixelformat.h"
+#include "graphics/decoders/image_decoder.h"
+#include "common/endian.h"
+#include "common/str.h"
+#include "common/stream.h"
+
+namespace Wintermute {
+class BaseSurface;
+class BaseFileManager;
+class BaseImage {
+
+public:
+ BaseImage();
+ ~BaseImage();
+
+ bool loadFile(const Common::String &filename);
+ const Graphics::Surface *getSurface() const {
+ return _surface;
+ };
+ const byte *getPalette() const {
+ return _palette;
+ }
+ byte getAlphaAt(int x, int y) const;
+ bool writeBMPToStream(Common::WriteStream *stream) const;
+ bool resize(int newWidth, int newHeight);
+ bool saveBMPFile(const char *filename) const;
+ bool copyFrom(BaseImage *origImage, int newWidth = 0, int newHeight = 0);
+ void copyFrom(const Graphics::Surface *surface);
+private:
+ Common::String _filename;
+ Graphics::ImageDecoder *_decoder;
+ const Graphics::Surface *_surface;
+ Graphics::Surface *_deletableSurface;
+ const byte *_palette;
+ BaseFileManager *_fileManager;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/base_renderer.cpp b/engines/wintermute/base/gfx/base_renderer.cpp
new file mode 100644
index 0000000000..e7ffc14c25
--- /dev/null
+++ b/engines/wintermute/base/gfx/base_renderer.cpp
@@ -0,0 +1,381 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_active_rect.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+BaseRenderer::BaseRenderer(BaseGame *inGame) : BaseClass(inGame) {
+ _window = 0;
+ _clipperWindow = 0;
+ _active = false;
+ _ready = false;
+ _windowed = true;
+ _forceAlphaColor = 0x00;
+
+ _indicatorDisplay = false;
+ _indicatorColor = BYTETORGBA(255, 0, 0, 128);
+ _indicatorProgress = 0;
+ _indicatorX = -1;
+ _indicatorY = -1;
+ _indicatorWidth = -1;
+ _indicatorHeight = 8;
+ _indicatorWidthDrawn = 0;
+
+ _loadImageName = "";
+ _saveImageName = "";
+ _saveLoadImage = NULL;
+ _loadInProgress = false;
+ _hasDrawnSaveLoadImage = false;
+
+ _saveImageX = _saveImageY = 0;
+ _loadImageX = _loadImageY = 0;
+
+ _width = _height = _bPP = 0;
+ BasePlatform::setRectEmpty(&_monitorRect);
+
+ _realWidth = _realHeight = 0;
+ _drawOffsetX = _drawOffsetY = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseRenderer::~BaseRenderer() {
+ deleteRectList();
+ unclipCursor();
+ delete _saveLoadImage;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseRenderer::initLoop() {
+ deleteRectList();
+}
+
+void BaseRenderer::initIndicator() {
+ if (_indicatorY == -1) {
+ _indicatorY = _height - _indicatorHeight;
+ }
+ if (_indicatorX == -1) {
+ _indicatorX = 0;
+ }
+ if (_indicatorWidth == -1) {
+ _indicatorWidth = _width;
+ }
+}
+
+void BaseRenderer::setIndicator(int width, int height, int x, int y, uint32 color) {
+ _indicatorWidth = width;
+ _indicatorHeight = height;
+ _indicatorX = x;
+ _indicatorY = y;
+ _indicatorColor = color;
+}
+
+void BaseRenderer::setIndicatorVal(int value) {
+ bool redisplay = (_indicatorProgress != value);
+ _indicatorProgress = value;
+ if (redisplay)
+ displayIndicator();
+}
+
+void BaseRenderer::setLoadingScreen(const char *filename, int x, int y) {
+ // TODO: Handle NULL
+ _loadImageName = filename;
+ _loadImageX = x;
+ _loadImageY = y;
+}
+
+void BaseRenderer::setSaveImage(const char *filename, int x, int y) {
+ // TODO: Handle NULL
+ _saveImageName = filename;
+ _saveImageX = x;
+ _saveImageY = y;
+}
+
+void BaseRenderer::initSaveLoad(bool isSaving, bool quickSave) {
+ _indicatorDisplay = true;
+ _indicatorProgress = 0;
+ _hasDrawnSaveLoadImage = false;
+
+ if (isSaving && !quickSave) {
+ delete _saveLoadImage;
+ _saveLoadImage = NULL;
+ if (_saveImageName.size()) {
+ _saveLoadImage = createSurface();
+
+ if (!_saveLoadImage || DID_FAIL(_saveLoadImage->create(_saveImageName, true, 0, 0, 0))) {
+ delete _saveLoadImage;
+ _saveLoadImage = NULL;
+ }
+ }
+ } else {
+ delete _saveLoadImage;
+ _saveLoadImage = NULL;
+ if (_loadImageName.size()) {
+ _saveLoadImage = createSurface();
+
+ if (!_saveLoadImage || DID_FAIL(_saveLoadImage->create(_loadImageName, true, 0, 0, 0))) {
+ delete _saveLoadImage;
+ _saveLoadImage = NULL;
+ }
+ }
+ _loadInProgress = true;
+ }
+}
+
+void BaseRenderer::endSaveLoad() {
+ _loadInProgress = false;
+ _indicatorDisplay = false;
+ _indicatorWidthDrawn = 0;
+
+ delete _saveLoadImage;
+ _saveLoadImage = NULL;
+}
+
+void BaseRenderer::persistSaveLoadImages(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_loadImageName));
+ persistMgr->transfer(TMEMBER(_saveImageName));
+ persistMgr->transfer(TMEMBER(_saveImageX));
+ persistMgr->transfer(TMEMBER(_saveImageY));
+ persistMgr->transfer(TMEMBER(_loadImageX));
+ persistMgr->transfer(TMEMBER(_loadImageY));
+}
+
+//////////////////////////////////////////////////////////////////////
+BaseObject *BaseRenderer::getObjectAt(int x, int y) {
+ Point32 point;
+ point.x = x;
+ point.y = y;
+
+ for (int i = _rectList.size() - 1; i >= 0; i--) {
+ if (BasePlatform::ptInRect(&_rectList[i]->_rect, point)) {
+ if (_rectList[i]->_precise) {
+ // frame
+ if (_rectList[i]->_frame) {
+ int xx = (int)((_rectList[i]->_frame->getRect().left + x - _rectList[i]->_rect.left + _rectList[i]->_offsetX) / (float)((float)_rectList[i]->_zoomX / (float)100));
+ int yy = (int)((_rectList[i]->_frame->getRect().top + y - _rectList[i]->_rect.top + _rectList[i]->_offsetY) / (float)((float)_rectList[i]->_zoomY / (float)100));
+
+ if (_rectList[i]->_frame->_mirrorX) {
+ int width = _rectList[i]->_frame->getRect().right - _rectList[i]->_frame->getRect().left;
+ xx = width - xx;
+ }
+
+ if (_rectList[i]->_frame->_mirrorY) {
+ int height = _rectList[i]->_frame->getRect().bottom - _rectList[i]->_frame->getRect().top;
+ yy = height - yy;
+ }
+
+ if (!_rectList[i]->_frame->_surface->isTransparentAt(xx, yy)) {
+ return _rectList[i]->_owner;
+ }
+ }
+ // region
+ else if (_rectList[i]->_region) {
+ if (_rectList[i]->_region->pointInRegion(x + _rectList[i]->_offsetX, y + _rectList[i]->_offsetY)) {
+ return _rectList[i]->_owner;
+ }
+ }
+ } else {
+ return _rectList[i]->_owner;
+ }
+ }
+ }
+
+ return (BaseObject *)NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseRenderer::deleteRectList() {
+ for (uint32 i = 0; i < _rectList.size(); i++) {
+ delete _rectList[i];
+ }
+ _rectList.clear();
+}
+
+//////////////////////////////////////////////////////////////////////
+bool BaseRenderer::initRenderer(int width, int height, bool windowed) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void BaseRenderer::onWindowChange() {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::windowedBlt() {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::setup2D(bool Force) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::setupLines() {
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::drawRect(int x1, int y1, int x2, int y2, uint32 color, int width) {
+ for (int i = 0; i < width; i++) {
+ drawLine(x1 + i, y1 + i, x2 - i, y1 + i, color); // up
+ drawLine(x1 + i, y2 - i, x2 - i + 1, y2 - i, color); // down
+
+ drawLine(x1 + i, y1 + i, x1 + i, y2 - i, color); // left
+ drawLine(x2 - i, y1 + i, x2 - i, y2 - i + 1, color); // right
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::setViewport(int left, int top, int right, int bottom) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::setScreenViewport() {
+ return setViewport(_drawOffsetX, _drawOffsetY, _width + _drawOffsetX, _height + _drawOffsetY);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::setViewport(Rect32 *rect) {
+ return setViewport(rect->left + _drawOffsetX,
+ rect->top + _drawOffsetY,
+ rect->right + _drawOffsetX,
+ rect->bottom + _drawOffsetY);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::clipCursor() {
+ // TODO: Reimplement this. (Currently aspect-indpendence isn't quite finished)
+ /*
+ if (!_windowed) {
+ Rect32 rc;
+ GetWindowRect(_window, &rc);
+
+ // if "maintain aspect ratio" is in effect, lock mouse to visible area
+ rc.left = _drawOffsetX;
+ rc.top = _drawOffsetY;
+ rc.right = rc.left + _width;
+ rc.bottom = rc.top + _height;
+
+ ::ClipCursor(&rc);
+ }
+ */
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::unclipCursor() {
+ /*
+ if (!_windowed) ::ClipCursor(NULL);
+ */
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::pointInViewport(Point32 *p) {
+ if (p->x < _drawOffsetX) {
+ return false;
+ }
+ if (p->y < _drawOffsetY) {
+ return false;
+ }
+ if (p->x > _drawOffsetX + _width) {
+ return false;
+ }
+ if (p->y > _drawOffsetY + _height) {
+ return false;
+ }
+
+ return true;
+}
+
+void BaseRenderer::addRectToList(BaseActiveRect *rect) {
+ _rectList.push_back(rect);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderer::displayIndicator() {
+ if (!_indicatorDisplay || !_indicatorProgress) {
+ return STATUS_OK;
+ }
+ if (_saveLoadImage && !_hasDrawnSaveLoadImage) {
+ Rect32 rc;
+ BasePlatform::setRect(&rc, 0, 0, _saveLoadImage->getWidth(), _saveLoadImage->getHeight());
+ if (_loadInProgress) {
+ _saveLoadImage->displayTrans(_loadImageX, _loadImageY, rc);
+ } else {
+ _saveLoadImage->displayTrans(_saveImageX, _saveImageY, rc);
+ }
+ flip();
+ _hasDrawnSaveLoadImage = true;
+ }
+
+ if ((!_indicatorDisplay && _indicatorWidth <= 0) || _indicatorHeight <= 0) {
+ return STATUS_OK;
+ }
+ setupLines();
+ int curWidth = (int)(_indicatorWidth * (float)((float)_indicatorProgress / 100.0f));
+ for (int i = 0; i < _indicatorHeight; i++) {
+ drawLine(_indicatorX, _indicatorY + i, _indicatorX + curWidth, _indicatorY + i, _indicatorColor);
+ }
+
+ setup2D();
+ _indicatorWidthDrawn = curWidth;
+ if (_indicatorWidthDrawn) {
+ indicatorFlip();
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/gfx/base_renderer.h b/engines/wintermute/base/gfx/base_renderer.h
new file mode 100644
index 0000000000..0475824464
--- /dev/null
+++ b/engines/wintermute/base/gfx/base_renderer.h
@@ -0,0 +1,221 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_RENDERER_H
+#define WINTERMUTE_BASE_RENDERER_H
+
+#include "engines/wintermute/math/rect32.h"
+#include "engines/wintermute/base/base.h"
+#include "common/rect.h"
+#include "common/array.h"
+
+namespace Wintermute {
+
+class BaseImage;
+class BaseActiveRect;
+class BaseObject;
+class BaseSurface;
+class BasePersistenceManager;
+
+/**
+ * @class BaseRenderer a common interface for the rendering portion of WME
+ * this interface is mainly intended to wrap away any differencies between
+ * software-rendering/hardware-rendering.
+ */
+class BaseRenderer: public BaseClass {
+public:
+ int _realWidth;
+ int _realHeight;
+ int _drawOffsetX;
+ int _drawOffsetY;
+
+ virtual void dumpData(const char *filename) {}
+ /**
+ * Take a screenshot of the current screenstate
+ *
+ * @return a BaseImage containing the current screen-buffer.
+ */
+ virtual BaseImage *takeScreenshot() = 0;
+ virtual bool setViewport(int left, int top, int right, int bottom);
+ virtual bool setViewport(Rect32 *rect);
+ virtual Rect32 getViewPort() = 0;
+ virtual bool setScreenViewport();
+
+ virtual Graphics::PixelFormat getPixelFormat() const = 0;
+ /**
+ * Fade the screen to black
+ *
+ * @param alpha amount to fade by (alpha value of black)
+ * @return
+ */
+ virtual void fade(uint16 alpha) = 0;
+ /**
+ * Fade a portion of the screen to a specific color
+ *
+ * @param r the red component to fade too.
+ * @param g the green component to fade too.
+ * @param b the blue component to fade too.
+ * @param a the alpha component to fade too.
+ * @param rect the portion of the screen to fade (if NULL, the entire screen will be faded).
+ */
+ virtual void fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect = NULL) = 0;
+ virtual bool drawLine(int x1, int y1, int x2, int y2, uint32 color);
+ virtual bool drawRect(int x1, int y1, int x2, int y2, uint32 color, int width = 1);
+ BaseRenderer(BaseGame *inGame = NULL);
+ virtual ~BaseRenderer();
+ virtual bool setProjection() {
+ return STATUS_OK;
+ };
+
+ virtual bool windowedBlt();
+ /**
+ * Fill a portion of the screen with a specified color
+ *
+ * @param r the red component to fill with.
+ * @param g the green component to fill with.
+ * @param b the blue component to fill with.
+ */
+ virtual bool fill(byte r, byte g, byte b, Common::Rect *rect = NULL) = 0;
+ virtual void onWindowChange();
+ virtual bool initRenderer(int width, int height, bool windowed);
+ /**
+ * Flip the backbuffer onto the screen-buffer
+ * The screen will NOT be updated before calling this function.
+ *
+ * @return true if successfull, false on error.
+ */
+ virtual bool flip() = 0;
+ /**
+ * Special flip for the indicator drawn during save/load
+ * essentially, just copies the region defined by the _indicator-variables.
+ */
+ virtual bool indicatorFlip() = 0;
+ virtual void initLoop();
+ virtual bool setup2D(bool force = false);
+ virtual bool setupLines();
+
+ /**
+ * Get the name of the current renderer
+ *
+ * @return the name of the renderer.
+ */
+ virtual Common::String getName() const = 0;
+ virtual bool displayDebugInfo() {
+ return STATUS_FAILED;
+ };
+ virtual bool drawShaderQuad() {
+ return STATUS_FAILED;
+ }
+
+ virtual float getScaleRatioX() const {
+ return 1.0f;
+ }
+ virtual float getScaleRatioY() const {
+ return 1.0f;
+ }
+
+ /**
+ * Create a Surface fit for use with the renderer.
+ * As diverse implementations of BaseRenderer might have different solutions for storing surfaces
+ * this allows for a common interface for creating surface-handles. (Mostly usefull to ease future
+ * implementation of hw-accelerated rendering, or readding 3D-support at some point).
+ *
+ * @return a surface that can be used with this renderer
+ */
+ virtual BaseSurface *createSurface() = 0;
+
+ bool clipCursor();
+ bool unclipCursor();
+
+ BaseObject *getObjectAt(int x, int y);
+ void deleteRectList();
+
+ virtual bool startSpriteBatch() {
+ return STATUS_OK;
+ };
+ virtual bool endSpriteBatch() {
+ return STATUS_OK;
+ };
+ bool pointInViewport(Point32 *P);
+ bool _active;
+ bool _ready;
+ bool _windowed;
+ int _bPP;
+ int _height;
+ int _width;
+ uint32 _window;
+ uint32 _forceAlphaColor;
+
+ void addRectToList(BaseActiveRect *rect);
+
+ // Indicator & Save/Load-related functions
+ void initIndicator();
+ void setIndicatorVal(int value);
+ void setIndicator(int width, int height, int x, int y, uint32 color);
+ void persistSaveLoadImages(BasePersistenceManager *persistMgr);
+ void initSaveLoad(bool isSaving, bool quickSave = false);
+ void endSaveLoad();
+ void setLoadingScreen(const char *filename, int x, int y);
+ void setSaveImage(const char *filename, int x, int y);
+
+ bool displayIndicator();
+protected:
+ Common::String _loadImageName;
+ Common::String _saveImageName;
+ int _saveImageX;
+ int _saveImageY;
+ int _loadImageX;
+ int _loadImageY;
+ BaseSurface *_saveLoadImage;
+ bool _hasDrawnSaveLoadImage;
+
+ int _indicatorWidthDrawn;
+ uint32 _indicatorColor;
+ int _indicatorX;
+ int _indicatorY;
+ int _indicatorWidth;
+ int _indicatorHeight;
+ bool _loadInProgress;
+ bool _indicatorDisplay;
+ int _indicatorProgress;
+protected:
+ uint32 _clipperWindow;
+
+ Rect32 _windowRect;
+ Rect32 _viewportRect;
+ Rect32 _screenRect;
+ Rect32 _monitorRect;
+private:
+ Common::Array<BaseActiveRect *> _rectList;
+};
+
+BaseRenderer *makeOSystemRenderer(BaseGame *inGame); // Implemented in BRenderSDL.cpp
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/base_surface.cpp b/engines/wintermute/base/gfx/base_surface.cpp
new file mode 100644
index 0000000000..d882adf5ad
--- /dev/null
+++ b/engines/wintermute/base/gfx/base_surface.cpp
@@ -0,0 +1,149 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/wintypes.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+BaseSurface::BaseSurface(BaseGame *inGame) : BaseClass(inGame) {
+ _referenceCount = 0;
+
+ _width = _height = 0;
+
+ _filename = "";
+
+ _pixelOpReady = false;
+
+ _ckDefault = true;
+ _ckRed = _ckGreen = _ckBlue = 0;
+ _lifeTime = 0;
+ _keepLoaded = false;
+
+ _lastUsedTime = 0;
+ _valid = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+BaseSurface::~BaseSurface() {
+ if (_pixelOpReady) {
+ endPixelOp();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSurface::restore() {
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSurface::isTransparentAt(int x, int y) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSurface::displayHalfTrans(int x, int y, Rect32 rect) {
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+ return displayTransZoom(x, y, rect, zoomX, zoomY, alpha, blendMode, mirrorX, mirrorY);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::create(int width, int height) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::startPixelOp() {
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::endPixelOp() {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::putPixel(int x, int y, byte r, byte g, byte b, int a) {
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::comparePixel(int x, int y, byte r, byte g, byte b, int a) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+bool BaseSurface::isTransparentAtLite(int x, int y) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::invalidate() {
+ return STATUS_FAILED;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurface::prepareToDraw() {
+ _lastUsedTime = _gameRef->_liveTimer;
+
+ if (!_valid) {
+ //_gameRef->LOG(0, "Reviving: %s", _filename);
+ return create(_filename.c_str(), _ckDefault, _ckRed, _ckGreen, _ckBlue, _lifeTime, _keepLoaded);
+ } else {
+ return STATUS_OK;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSurface::setSize(int width, int height) {
+ _width = width;
+ _height = height;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/gfx/base_surface.h b/engines/wintermute/base/gfx/base_surface.h
new file mode 100644
index 0000000000..ee53c03e77
--- /dev/null
+++ b/engines/wintermute/base/gfx/base_surface.h
@@ -0,0 +1,99 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SURFACE_H
+#define WINTERMUTE_BASE_SURFACE_H
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/math/rect32.h"
+#include "graphics/surface.h"
+
+namespace Wintermute {
+
+class BaseSurface: public BaseClass {
+public:
+ virtual bool invalidate();
+ virtual bool prepareToDraw();
+ uint32 _lastUsedTime;
+ bool _valid;
+ int _lifeTime;
+
+ bool _pixelOpReady;
+ BaseSurface(BaseGame *inGame);
+ virtual ~BaseSurface();
+
+ virtual bool displayHalfTrans(int x, int y, Rect32 rect);
+ virtual bool isTransparentAt(int x, int y);
+ virtual bool displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0;
+ virtual bool displayTrans(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0;
+ virtual bool displayTransOffset(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false, int offsetX = 0, int offsetY = 0) = 0;
+ virtual bool display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0;
+ virtual bool displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, bool transparent = false, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0;
+ virtual bool displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0;
+ virtual bool restore();
+ virtual bool create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime = -1, bool keepLoaded = false) = 0;
+ virtual bool create(int width, int height);
+ virtual bool putSurface(const Graphics::Surface &surface, bool hasAlpha = false) {
+ return STATUS_FAILED;
+ }
+ virtual bool putPixel(int x, int y, byte r, byte g, byte b, int a = -1);
+ virtual bool getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a = NULL);
+ virtual bool comparePixel(int x, int y, byte r, byte g, byte b, int a = -1);
+ virtual bool startPixelOp();
+ virtual bool endPixelOp();
+ virtual bool isTransparentAtLite(int x, int y);
+ void setSize(int width, int height);
+
+ int _referenceCount;
+
+ virtual int getWidth() {
+ return _width;
+ }
+ virtual int getHeight() {
+ return _height;
+ }
+ Common::String getFileNameStr() { return _filename; }
+ const char* getFileName() { return _filename.c_str(); }
+ //void SetWidth(int Width){ _width = Width; }
+ //void SetHeight(int Height){ _height = Height; }
+protected:
+ bool _ckDefault;
+ byte _ckRed;
+ byte _ckGreen;
+ byte _ckBlue;
+
+ bool _keepLoaded;
+ Common::String _filename;
+ int _height;
+ int _width;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp
new file mode 100644
index 0000000000..7970a25300
--- /dev/null
+++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp
@@ -0,0 +1,609 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h"
+#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h"
+#include "engines/wintermute/base/base_surface_storage.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/math/math_util.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "common/system.h"
+#include "engines/wintermute/graphics/transparent_surface.h"
+#include "common/queue.h"
+#include "common/config-manager.h"
+
+namespace Wintermute {
+
+RenderTicket::RenderTicket(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha) : _owner(owner),
+ _srcRect(*srcRect), _dstRect(*dstRect), _drawNum(0), _isValid(true), _wantsDraw(true), _hasAlpha(!disableAlpha) {
+ _colorMod = 0;
+ _mirror = TransparentSurface::FLIP_NONE;
+ if (mirrorX) {
+ _mirror |= TransparentSurface::FLIP_V;
+ }
+ if (mirrorY) {
+ _mirror |= TransparentSurface::FLIP_H;
+ }
+ if (surf) {
+ _surface = new Graphics::Surface();
+ _surface->create((uint16)srcRect->width(), (uint16)srcRect->height(), surf->format);
+ assert(_surface->format.bytesPerPixel == 4);
+ // Get a clipped copy of the surface
+ for (int i = 0; i < _surface->h; i++) {
+ memcpy(_surface->getBasePtr(0, i), surf->getBasePtr(srcRect->left, srcRect->top + i), srcRect->width() * _surface->format.bytesPerPixel);
+ }
+ // Then scale it if necessary
+ if (dstRect->width() != srcRect->width() || dstRect->height() != srcRect->height()) {
+ TransparentSurface src(*_surface, false);
+ Graphics::Surface *temp = src.scale(dstRect->width(), dstRect->height());
+ _surface->free();
+ delete _surface;
+ _surface = temp;
+ }
+ } else {
+ _surface = NULL;
+ }
+}
+
+RenderTicket::~RenderTicket() {
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+}
+
+bool RenderTicket::operator==(RenderTicket &t) {
+ if ((t._srcRect != _srcRect) ||
+ (t._dstRect != _dstRect) ||
+ (t._mirror != _mirror) ||
+ (t._owner != _owner) ||
+ (t._hasAlpha != _hasAlpha) ||
+ (t._colorMod != _colorMod)) {
+ return false;
+ }
+ return true;
+}
+
+BaseRenderer *makeOSystemRenderer(BaseGame *inGame) {
+ return new BaseRenderOSystem(inGame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseRenderOSystem::BaseRenderOSystem(BaseGame *inGame) : BaseRenderer(inGame) {
+ _renderSurface = new Graphics::Surface();
+ _blankSurface = new Graphics::Surface();
+ _drawNum = 1;
+ _needsFlip = true;
+
+ _borderLeft = _borderRight = _borderTop = _borderBottom = 0;
+ _ratioX = _ratioY = 1.0f;
+ setAlphaMod(255);
+ setColorMod(255, 255, 255);
+ _dirtyRect = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseRenderOSystem::~BaseRenderOSystem() {
+ _renderSurface->free();
+ delete _renderSurface;
+ _blankSurface->free();
+ delete _blankSurface;
+ TransparentSurface::destroyLookup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderOSystem::initRenderer(int width, int height, bool windowed) {
+ _width = width;
+ _height = height;
+ _renderRect.setWidth(_width);
+ _renderRect.setHeight(_height);
+
+ _realWidth = width;
+ _realHeight = height;
+
+ //TODO: Tiny resolution-displays might want to do some resolution-selection logic here
+
+ //_realWidth = BaseEngine::instance().getRegistry()->readInt("Debug", "ForceResWidth", _width);
+ //_realHeight = BaseEngine::instance().getRegistry()->readInt("Debug", "ForceResHeight", _height);
+
+ float origAspect = (float)_width / (float)_height;
+ float realAspect = (float)_realWidth / (float)_realHeight;
+
+ float ratio;
+ if (origAspect < realAspect) {
+ // normal to wide
+ ratio = (float)_realHeight / (float)_height;
+ } else {
+ // wide to normal
+ ratio = (float)_realWidth / (float)_width;
+ }
+
+ _borderLeft = (int)((_realWidth - (_width * ratio)) / 2);
+ _borderRight = (int)(_realWidth - (_width * ratio) - _borderLeft);
+
+ _borderTop = (int)((_realHeight - (_height * ratio)) / 2);
+ _borderBottom = (int)(_realHeight - (_height * ratio) - _borderTop);
+
+
+
+ _ratioX = (float)(_realWidth - _borderLeft - _borderRight) / (float)_width;
+ _ratioY = (float)(_realHeight - _borderTop - _borderBottom) / (float)_height;
+
+ _windowed = !ConfMan.getBool("fullscreen");
+
+ Graphics::PixelFormat format(4, 8, 8, 8, 8, 16, 8, 0, 24);
+ g_system->beginGFXTransaction();
+ g_system->initSize(_width, _height, &format);
+ OSystem::TransactionError gfxError = g_system->endGFXTransaction();
+
+ if (gfxError != OSystem::kTransactionSuccess) {
+ warning("Couldn't setup GFX-backend for %dx%dx%d", _width, _height, format.bytesPerPixel * 8);
+ return STATUS_FAILED;
+ }
+
+ g_system->showMouse(false);
+
+ _renderSurface->create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat());
+ _blankSurface->create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat());
+ _blankSurface->fillRect(Common::Rect(0, 0, _blankSurface->h, _blankSurface->w), _blankSurface->format.ARGBToColor(255, 0, 0, 0));
+ _active = true;
+
+ _clearColor = _renderSurface->format.ARGBToColor(255, 0, 0, 0);
+
+ return STATUS_OK;
+}
+
+void BaseRenderOSystem::setAlphaMod(byte alpha) {
+ byte r = RGBCOLGetR(_colorMod);
+ byte g = RGBCOLGetB(_colorMod);
+ byte b = RGBCOLGetB(_colorMod);
+ _colorMod = BS_ARGB(alpha, r, g, b);
+}
+
+void BaseRenderOSystem::setColorMod(byte r, byte g, byte b) {
+ byte alpha = RGBCOLGetA(_colorMod);
+ _colorMod = BS_ARGB(alpha, r, g, b);
+}
+
+bool BaseRenderOSystem::indicatorFlip() {
+ g_system->copyRectToScreen((byte *)_renderSurface->getBasePtr(_indicatorX, _indicatorY), _renderSurface->pitch, _indicatorX, _indicatorY, _indicatorWidthDrawn, _indicatorHeight);
+ g_system->updateScreen();
+ return STATUS_OK;
+}
+
+bool BaseRenderOSystem::flip() {
+ if (!_disableDirtyRects) {
+ drawTickets();
+ } else {
+ // Clear the scale-buffered tickets that wasn't reused.
+ RenderQueueIterator it = _renderQueue.begin();
+ while (it != _renderQueue.end()) {
+ if ((*it)->_wantsDraw == false) {
+ RenderTicket *ticket = *it;
+ it = _renderQueue.erase(it);
+ delete ticket;
+ } else {
+ (*it)->_wantsDraw = false;
+ ++it;
+ }
+ }
+ }
+ if (_needsFlip || _disableDirtyRects) {
+ if (_disableDirtyRects) {
+ g_system->copyRectToScreen((byte *)_renderSurface->pixels, _renderSurface->pitch, 0, 0, _renderSurface->w, _renderSurface->h);
+ }
+ // g_system->copyRectToScreen((byte *)_renderSurface->pixels, _renderSurface->pitch, _dirtyRect->left, _dirtyRect->top, _dirtyRect->width(), _dirtyRect->height());
+ delete _dirtyRect;
+ _dirtyRect = NULL;
+ g_system->updateScreen();
+ _needsFlip = false;
+ }
+ _drawNum = 1;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderOSystem::fill(byte r, byte g, byte b, Common::Rect *rect) {
+ _clearColor = _renderSurface->format.ARGBToColor(0xFF, r, g, b);
+ if (!_disableDirtyRects) {
+ return STATUS_OK;
+ }
+ if (!rect) {
+// TODO: This should speed things up, but for some reason it misses the size by quite a bit.
+/* if (r == 0 && g == 0 && b == 0) {
+ // Simply memcpy from the buffered black-surface, way faster than Surface::fillRect.
+ memcpy(_renderSurface->pixels, _blankSurface->pixels, _renderSurface->pitch * _renderSurface->h);
+ return STATUS_OK;
+ }*/
+ rect = &_renderRect;
+ }
+ // TODO: This doesn't work with dirty rects
+ _renderSurface->fillRect(*rect, _clearColor);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseRenderOSystem::fade(uint16 alpha) {
+ byte dwAlpha = (byte)(255 - alpha);
+ return fadeToColor(0, 0, 0, dwAlpha);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseRenderOSystem::fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect) {
+ // This particular warning is rather messy, as this function is called a ton,
+ // thus we avoid printing it more than once.
+
+ // TODO: Add fading with dirty rects.
+ if (!_disableDirtyRects) {
+ warning("BaseRenderOSystem::FadeToColor - Breaks when using dirty rects");
+ }
+
+ Common::Rect fillRect;
+
+ if (rect) {
+ fillRect.left = rect->left;
+ fillRect.top = rect->top;
+ fillRect.setWidth(rect->width());
+ fillRect.setHeight(rect->height());
+ } else {
+ Rect32 rc;
+ _gameRef->getCurrentViewportRect(&rc);
+ fillRect.left = (int16)rc.left;
+ fillRect.top = (int16)rc.top;
+ fillRect.setWidth((int16)(rc.right - rc.left));
+ fillRect.setHeight((int16)(rc.bottom - rc.top));
+ }
+ modTargetRect(&fillRect);
+
+ //TODO: This is only here until I'm sure about the final pixelformat
+ uint32 col = _renderSurface->format.ARGBToColor(a, r, g, b);
+
+ setAlphaMod(255);
+ setColorMod(255, 255, 255);
+ Graphics::Surface surf;
+ surf.create((uint16)fillRect.width(), (uint16)fillRect.height(), _renderSurface->format);
+ Common::Rect sizeRect(fillRect);
+ sizeRect.translate(-fillRect.top, -fillRect.left);
+ surf.fillRect(fillRect, col);
+ drawSurface(NULL, &surf, &sizeRect, &fillRect, false, false);
+ surf.free();
+
+ //SDL_SetRenderDrawColor(_renderer, r, g, b, a);
+ //SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND);
+ //SDL_RenderFillRect(_renderer, &fillRect);
+}
+
+Graphics::PixelFormat BaseRenderOSystem::getPixelFormat() const {
+ return _renderSurface->format;
+}
+
+void BaseRenderOSystem::drawSurface(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha) {
+ // Skip rects that are completely outside the screen:
+ if ((dstRect->left < 0 && dstRect->right < 0) || (dstRect->top < 0 && dstRect->bottom < 0)) {
+ return;
+ }
+
+ if (owner) { // Fade-tickets are owner-less
+ RenderTicket compare(owner, NULL, srcRect, dstRect, mirrorX, mirrorY, disableAlpha);
+ compare._colorMod = _colorMod;
+ RenderQueueIterator it;
+ for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) {
+ if ((*it)->_owner == owner && *(*it) == compare && (*it)->_isValid) {
+ (*it)->_colorMod = _colorMod;
+ if (_disableDirtyRects) {
+ drawFromSurface(*it, NULL);
+ } else {
+ drawFromTicket(*it);
+ }
+ return;
+ }
+ }
+ }
+ RenderTicket *ticket = new RenderTicket(owner, surf, srcRect, dstRect, mirrorX, mirrorY, disableAlpha);
+ ticket->_colorMod = _colorMod;
+ if (!_disableDirtyRects) {
+ drawFromTicket(ticket);
+ } else {
+ ticket->_wantsDraw = true;
+ _renderQueue.push_back(ticket);
+ drawFromSurface(ticket, NULL);
+ }
+}
+
+void BaseRenderOSystem::invalidateTicket(RenderTicket *renderTicket) {
+ addDirtyRect(renderTicket->_dstRect);
+ renderTicket->_isValid = false;
+// renderTicket->_canDelete = true; // TODO: Maybe readd this, to avoid even more duplicates.
+}
+
+void BaseRenderOSystem::invalidateTicketsFromSurface(BaseSurfaceOSystem *surf) {
+ RenderQueueIterator it;
+ for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) {
+ if ((*it)->_owner == surf) {
+ invalidateTicket(*it);
+ }
+ }
+}
+
+void BaseRenderOSystem::drawFromTicket(RenderTicket *renderTicket) {
+ renderTicket->_wantsDraw = true;
+ // A new item always has _drawNum == 0
+ if (renderTicket->_drawNum == 0) {
+ // In-order
+ if (_renderQueue.empty() || _drawNum > (_renderQueue.back())->_drawNum) {
+ renderTicket->_drawNum = _drawNum++;
+ _renderQueue.push_back(renderTicket);
+ addDirtyRect(renderTicket->_dstRect);
+ } else {
+ // Before something
+ Common::List<RenderTicket *>::iterator pos;
+ for (pos = _renderQueue.begin(); pos != _renderQueue.end(); pos++) {
+ if ((*pos)->_drawNum >= _drawNum) {
+ break;
+ }
+ }
+ _renderQueue.insert(pos, renderTicket);
+ Common::List<RenderTicket *>::iterator it;
+ renderTicket->_drawNum = _drawNum++;
+ // Increment the following tickets, so they still are in line
+ for (it = pos; it != _renderQueue.end(); ++it) {
+ (*it)->_drawNum++;
+ (*it)->_wantsDraw = false;
+ }
+ addDirtyRect(renderTicket->_dstRect);
+ }
+ } else {
+ // Was drawn last round, still in the same order
+ if (_drawNum == renderTicket->_drawNum) {
+ _drawNum++;
+ } else {
+ // Remove the ticket from the list
+ RenderQueueIterator it = _renderQueue.begin();
+ while (it != _renderQueue.end()) {
+ if ((*it) == renderTicket) {
+ it = _renderQueue.erase(it);
+ break;
+ } else {
+ ++it;
+ }
+ }
+ if (it != _renderQueue.end()) {
+ // Decreement the following tickets.
+ for (; it != _renderQueue.end(); ++it) {
+ (*it)->_drawNum--;
+ }
+ }
+ // Is not in order, so readd it as if it was a new ticket
+ renderTicket->_drawNum = 0;
+ drawFromTicket(renderTicket);
+ }
+ }
+}
+
+void BaseRenderOSystem::addDirtyRect(const Common::Rect &rect) {
+ if (!_dirtyRect) {
+ _dirtyRect = new Common::Rect(rect);
+ } else {
+ _dirtyRect->extend(rect);
+ }
+ _dirtyRect->clip(_renderRect);
+}
+
+void BaseRenderOSystem::drawTickets() {
+ RenderQueueIterator it = _renderQueue.begin();
+ // Clean out the old tickets
+ int decrement = 0;
+ while (it != _renderQueue.end()) {
+ if ((*it)->_wantsDraw == false || (*it)->_isValid == false) {
+ RenderTicket *ticket = *it;
+ addDirtyRect((*it)->_dstRect);
+ it = _renderQueue.erase(it);
+ delete ticket;
+ decrement++;
+ } else {
+ (*it)->_drawNum -= decrement;
+ ++it;
+ }
+ }
+ if (!_dirtyRect || _dirtyRect->width() == 0 || _dirtyRect->height() == 0) {
+ return;
+ }
+ // The color-mods are stored in the RenderTickets on add, since we set that state again during
+ // draw, we need to keep track of what it was prior to draw.
+ uint32 oldColorMod = _colorMod;
+
+ // Apply the clear-color to the dirty rect.
+ _renderSurface->fillRect(*_dirtyRect, _clearColor);
+ _drawNum = 1;
+ for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) {
+ RenderTicket *ticket = *it;
+ assert(ticket->_drawNum == _drawNum++);
+ if (ticket->_isValid && ticket->_dstRect.intersects(*_dirtyRect)) {
+ // dstClip is the area we want redrawn.
+ Common::Rect dstClip(ticket->_dstRect);
+ // reduce it to the dirty rect
+ dstClip.clip(*_dirtyRect);
+ // we need to keep track of the position to redraw the dirty rect
+ Common::Rect pos(dstClip);
+ int16 offsetX = ticket->_dstRect.left;
+ int16 offsetY = ticket->_dstRect.top;
+ // convert from screen-coords to surface-coords.
+ dstClip.translate(-offsetX, -offsetY);
+
+ _colorMod = ticket->_colorMod;
+ drawFromSurface(ticket->getSurface(), &ticket->_srcRect, &pos, &dstClip, ticket->_mirror);
+ _needsFlip = true;
+ }
+ // Some tickets want redraw but don't actually clip the dirty area (typically the ones that shouldnt become clear-color)
+ ticket->_wantsDraw = false;
+ }
+ g_system->copyRectToScreen((byte *)_renderSurface->getBasePtr(_dirtyRect->left, _dirtyRect->top), _renderSurface->pitch, _dirtyRect->left, _dirtyRect->top, _dirtyRect->width(), _dirtyRect->height());
+
+ // Revert the colorMod-state.
+ _colorMod = oldColorMod;
+}
+
+// Replacement for SDL2's SDL_RenderCopy
+void BaseRenderOSystem::drawFromSurface(RenderTicket *ticket, Common::Rect *clipRect) {
+ TransparentSurface src(*ticket->getSurface(), false);
+ bool doDelete = false;
+ if (!clipRect) {
+ doDelete = true;
+ clipRect = new Common::Rect();
+ clipRect->setWidth(ticket->getSurface()->w);
+ clipRect->setHeight(ticket->getSurface()->h);
+ }
+
+ src._enableAlphaBlit = ticket->_hasAlpha;
+ src.blit(*_renderSurface, ticket->_dstRect.left, ticket->_dstRect.top, ticket->_mirror, clipRect, _colorMod, clipRect->width(), clipRect->height());
+ if (doDelete) {
+ delete clipRect;
+ }
+}
+void BaseRenderOSystem::drawFromSurface(const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, Common::Rect *clipRect, uint32 mirror) {
+ TransparentSurface src(*surf, false);
+ bool doDelete = false;
+ if (!clipRect) {
+ doDelete = true;
+ clipRect = new Common::Rect();
+ clipRect->setWidth(surf->w);
+ clipRect->setHeight(surf->h);
+ }
+
+ src.blit(*_renderSurface, dstRect->left, dstRect->top, mirror, clipRect, _colorMod, clipRect->width(), clipRect->height());
+ if (doDelete) {
+ delete clipRect;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderOSystem::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
+
+ if (!_disableDirtyRects) {
+ warning("BaseRenderOSystem::DrawLine - doesn't work for dirty rects yet");
+ }
+
+ byte r = RGBCOLGetR(color);
+ byte g = RGBCOLGetG(color);
+ byte b = RGBCOLGetB(color);
+ byte a = RGBCOLGetA(color);
+
+ //SDL_SetRenderDrawColor(_renderer, r, g, b, a);
+ //SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND);
+
+ Point32 point1, point2;
+ point1.x = x1;
+ point1.y = y1;
+ pointToScreen(&point1);
+
+ point2.x = x2;
+ point2.y = y2;
+ pointToScreen(&point2);
+
+ // TODO: This thing is mostly here until I'm sure about the final color-format.
+ uint32 colorVal = _renderSurface->format.ARGBToColor(a, r, g, b);
+ _renderSurface->drawLine(point1.x, point1.y, point2.x, point2.y, colorVal);
+ //SDL_RenderDrawLine(_renderer, point1.x, point1.y, point2.x, point2.y);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseImage *BaseRenderOSystem::takeScreenshot() {
+// TODO: Clip by viewport.
+ BaseImage *screenshot = new BaseImage();
+ screenshot->copyFrom(_renderSurface);
+ return screenshot;
+}
+
+//////////////////////////////////////////////////////////////////////////
+Common::String BaseRenderOSystem::getName() const {
+ return "ScummVM-OSystem-renderer";
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseRenderOSystem::setViewport(int left, int top, int right, int bottom) {
+ Common::Rect rect;
+ // TODO: Hopefully this is the same logic that ScummVM uses.
+ rect.left = (int16)(left + _borderLeft);
+ rect.top = (int16)(top + _borderTop);
+ rect.right = (int16)((right - left) * _ratioX);
+ rect.bottom = (int16)((bottom - top) * _ratioY);
+
+ _renderRect = rect;
+ return STATUS_OK;
+}
+
+Rect32 BaseRenderOSystem::getViewPort() {
+ Rect32 ret;
+ ret.top = _renderRect.top;
+ ret.bottom = _renderRect.bottom;
+ ret.left = _renderRect.left;
+ ret.right = _renderRect.right;
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseRenderOSystem::modTargetRect(Common::Rect *rect) {
+ // FIXME: This is wrong in quite a few ways right now, and ends up
+ // breaking the notebook in Dirty Split, so we disable the correction
+ // for now, this will need fixing when a game with odd aspect-ratios
+ // show up.
+ return;
+ rect->left = (int16)MathUtil::round(rect->left * _ratioX + _borderLeft - _renderRect.left);
+ rect->top = (int16)MathUtil::round(rect->top * _ratioY + _borderTop - _renderRect.top);
+ rect->setWidth((int16)MathUtil::roundUp(rect->width() * _ratioX));
+ rect->setHeight((int16)MathUtil::roundUp(rect->height() * _ratioY));
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseRenderOSystem::pointFromScreen(Point32 *point) {
+ point->x = (int16)(point->x / _ratioX - _borderLeft / _ratioX + _renderRect.left);
+ point->y = (int16)(point->y / _ratioY - _borderTop / _ratioY + _renderRect.top);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseRenderOSystem::pointToScreen(Point32 *point) {
+ point->x = (int16)MathUtil::roundUp(point->x * _ratioX) + _borderLeft - _renderRect.left;
+ point->y = (int16)MathUtil::roundUp(point->y * _ratioY) + _borderTop - _renderRect.top;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseRenderOSystem::dumpData(const char *filename) {
+ warning("BaseRenderOSystem::DumpData(%s) - stubbed", filename); // TODO
+}
+
+BaseSurface *BaseRenderOSystem::createSurface() {
+ return new BaseSurfaceOSystem(_gameRef);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.h b/engines/wintermute/base/gfx/osystem/base_render_osystem.h
new file mode 100644
index 0000000000..1e72508cd0
--- /dev/null
+++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.h
@@ -0,0 +1,131 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_RENDERER_SDL_H
+#define WINTERMUTE_BASE_RENDERER_SDL_H
+
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+#include "common/list.h"
+
+namespace Wintermute {
+class BaseSurfaceOSystem;
+class RenderTicket {
+ Graphics::Surface *_surface;
+public:
+ RenderTicket(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRest, bool mirrorX = false, bool mirrorY = false, bool disableAlpha = false);
+ RenderTicket() : _isValid(true), _wantsDraw(false), _drawNum(0) {}
+ ~RenderTicket();
+ const Graphics::Surface *getSurface() { return _surface; }
+ Common::Rect _srcRect;
+ Common::Rect _dstRect;
+ uint32 _mirror;
+ bool _hasAlpha;
+
+ bool _isValid;
+ bool _wantsDraw;
+ uint32 _drawNum;
+ uint32 _colorMod;
+
+ BaseSurfaceOSystem *_owner;
+ bool operator==(RenderTicket &a);
+};
+
+class BaseRenderOSystem : public BaseRenderer {
+public:
+ BaseRenderOSystem(BaseGame *inGame);
+ ~BaseRenderOSystem();
+
+ Common::String getName() const;
+
+ bool initRenderer(int width, int height, bool windowed);
+ bool flip();
+ virtual bool indicatorFlip();
+ bool fill(byte r, byte g, byte b, Common::Rect *rect = NULL);
+ Graphics::PixelFormat getPixelFormat() const;
+ void fade(uint16 alpha);
+ void fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect = NULL);
+
+ bool drawLine(int x1, int y1, int x2, int y2, uint32 color);
+
+ BaseImage *takeScreenshot();
+
+ void setAlphaMod(byte alpha);
+ void setColorMod(byte r, byte g, byte b);
+ void invalidateTicket(RenderTicket *renderTicket);
+ void invalidateTicketsFromSurface(BaseSurfaceOSystem *surf);
+ void drawFromTicket(RenderTicket *renderTicket);
+
+ bool setViewport(int left, int top, int right, int bottom);
+ bool setViewport(Rect32 *rect) { return BaseRenderer::setViewport(rect); }
+ Rect32 getViewPort();
+ void modTargetRect(Common::Rect *rect);
+ void pointFromScreen(Point32 *point);
+ void pointToScreen(Point32 *point);
+
+ void dumpData(const char *filename);
+
+ float getScaleRatioX() const {
+ return _ratioX;
+ }
+ float getScaleRatioY() const {
+ return _ratioY;
+ }
+
+ void drawSurface(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha = false);
+ BaseSurface *createSurface();
+private:
+ void addDirtyRect(const Common::Rect &rect);
+ void drawTickets();
+ void drawFromSurface(RenderTicket *ticket, Common::Rect *clipRect);
+ void drawFromSurface(const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, Common::Rect *clipRect, uint32 mirror);
+ typedef Common::List<RenderTicket *>::iterator RenderQueueIterator;
+ Common::Rect *_dirtyRect;
+ Common::List<RenderTicket *> _renderQueue;
+ bool _needsFlip;
+ uint32 _drawNum;
+ Common::Rect _renderRect;
+ Graphics::Surface *_renderSurface;
+ Graphics::Surface *_blankSurface;
+
+ int _borderLeft;
+ int _borderTop;
+ int _borderRight;
+ int _borderBottom;
+
+ static const bool _disableDirtyRects = true;
+ float _ratioX;
+ float _ratioY;
+ uint32 _colorMod;
+ uint32 _clearColor;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp
new file mode 100644
index 0000000000..bee876bb65
--- /dev/null
+++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp
@@ -0,0 +1,428 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h"
+#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "graphics/decoders/png.h"
+#include "graphics/decoders/bmp.h"
+#include "graphics/decoders/jpeg.h"
+#include "graphics/decoders/tga.h"
+#include "engines/wintermute/graphics/transparent_surface.h"
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+#include "common/stream.h"
+#include "common/system.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+BaseSurfaceOSystem::BaseSurfaceOSystem(BaseGame *inGame) : BaseSurface(inGame) {
+ _surface = new Graphics::Surface();
+ _alphaMask = NULL;
+ _hasAlpha = true;
+ _lockPixels = NULL;
+ _lockPitch = 0;
+ _loaded = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseSurfaceOSystem::~BaseSurfaceOSystem() {
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ _surface = NULL;
+ }
+
+ delete[] _alphaMask;
+ _alphaMask = NULL;
+
+ _gameRef->addMem(-_width * _height * 4);
+ BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer);
+ renderer->invalidateTicketsFromSurface(this);
+}
+
+bool hasTransparency(Graphics::Surface *surf) {
+ if (surf->format.bytesPerPixel != 4) {
+ warning("hasTransparency:: non 32 bpp surface passed as argument");
+ return false;
+ }
+ uint8 r, g, b, a;
+ for (int i = 0; i < surf->h; i++) {
+ for (int j = 0; j < surf->w; j++) {
+ uint32 pix = *(uint32 *)surf->getBasePtr(j, i);
+ surf->format.colorToARGB(pix, a, r, g, b);
+ if (a != 255) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) {
+ /* BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); */
+ _filename = filename;
+// const Graphics::Surface *surface = image->getSurface();
+
+ if (defaultCK) {
+ ckRed = 255;
+ ckGreen = 0;
+ ckBlue = 255;
+ }
+
+ _ckDefault = defaultCK;
+ _ckRed = ckRed;
+ _ckGreen = ckGreen;
+ _ckBlue = ckBlue;
+
+ if (_lifeTime == 0 || lifeTime == -1 || lifeTime > _lifeTime) {
+ _lifeTime = lifeTime;
+ }
+
+ _keepLoaded = keepLoaded;
+ if (_keepLoaded) {
+ _lifeTime = -1;
+ }
+
+ return STATUS_OK;
+}
+
+bool BaseSurfaceOSystem::finishLoad() {
+ BaseImage *image = new BaseImage();
+ if (!image->loadFile(_filename)) {
+ return false;
+ }
+
+ _width = image->getSurface()->w;
+ _height = image->getSurface()->h;
+
+ bool isSaveGameGrayscale = scumm_strnicmp(_filename.c_str(), "savegame:", 9) == 0 && (_filename.c_str()[_filename.size() - 1] == 'g' || _filename.c_str()[_filename.size() - 1] == 'G');
+ if (isSaveGameGrayscale) {
+ warning("grayscaleConversion not yet implemented");
+ // FIBITMAP *newImg = FreeImage_ConvertToGreyscale(img); TODO
+ }
+
+ // no alpha, set color key
+ /* if (surface->format.bytesPerPixel != 4)
+ SDL_SetColorKey(surf, SDL_TRUE, SDL_MapRGB(surf->format, ck_red, ck_green, ck_blue));*/
+
+ // convert 32-bit BMPs to 24-bit or they appear totally transparent (does any app actually write alpha in BMP properly?)
+ // Well, actually, we don't convert via 24-bit as the color-key application overwrites the Alpha-channel anyhow.
+ _surface->free();
+ delete _surface;
+ if (_filename.hasSuffix(".bmp") && image->getSurface()->format.bytesPerPixel == 4) {
+ _surface = image->getSurface()->convertTo(g_system->getScreenFormat(), image->getPalette());
+ TransparentSurface trans(*_surface);
+ trans.applyColorKey(_ckRed, _ckGreen, _ckBlue);
+ } else if (image->getSurface()->format.bytesPerPixel == 1 && image->getPalette()) {
+ _surface = image->getSurface()->convertTo(g_system->getScreenFormat(), image->getPalette());
+ TransparentSurface trans(*_surface);
+ trans.applyColorKey(_ckRed, _ckGreen, _ckBlue, true);
+ } else if (image->getSurface()->format.bytesPerPixel >= 3 && image->getSurface()->format != g_system->getScreenFormat()) {
+ _surface = image->getSurface()->convertTo(g_system->getScreenFormat());
+ if (image->getSurface()->format.bytesPerPixel == 3) {
+ TransparentSurface trans(*_surface);
+ trans.applyColorKey(_ckRed, _ckGreen, _ckBlue, true);
+ }
+ } else {
+ _surface = new Graphics::Surface();
+ _surface->copyFrom(*image->getSurface());
+ }
+
+ _hasAlpha = hasTransparency(_surface);
+ _valid = true;
+
+ _gameRef->addMem(_width * _height * 4);
+
+ delete image;
+
+ _loaded = true;
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSurfaceOSystem::genAlphaMask(Graphics::Surface *surface) {
+ warning("BaseSurfaceOSystem::GenAlphaMask - Not ported yet");
+ return;
+ // TODO: Reimplement this
+ delete[] _alphaMask;
+ _alphaMask = NULL;
+ if (!surface) {
+ return;
+ }
+
+ bool hasColorKey;
+ /* uint32 colorKey; */
+ uint8 ckRed, ckGreen, ckBlue;
+ /* if (SDL_GetColorKey(surface, &colorKey) == 0) {
+ hasColorKey = true;
+ SDL_GetRGB(colorKey, surface->format, &ckRed, &ckGreen, &ckBlue);
+ } else hasColorKey = false;
+ */
+ _alphaMask = new byte[surface->w * surface->h];
+
+ bool hasTransparency = false;
+ for (int y = 0; y < surface->h; y++) {
+ for (int x = 0; x < surface->w; x++) {
+ uint32 pixel = getPixelAt(surface, x, y);
+
+ uint8 r, g, b, a;
+ surface->format.colorToARGB(pixel, a, r, g, b);
+ //SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a);
+
+ if (hasColorKey && r == ckRed && g == ckGreen && b == ckBlue) {
+ a = 0;
+ }
+
+ _alphaMask[y * surface->w + x] = a;
+ if (a < 255) {
+ hasTransparency = true;
+ }
+ }
+ }
+
+ if (!hasTransparency) {
+ delete[] _alphaMask;
+ _alphaMask = NULL;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseSurfaceOSystem::getPixelAt(Graphics::Surface *surface, int x, int y) {
+ warning("BaseSurfaceOSystem::GetPixel - Not ported yet");
+ int bpp = surface->format.bytesPerPixel;
+ /* Here p is the address to the pixel we want to retrieve */
+ uint8 *p = (uint8 *)surface->pixels + y * surface->pitch + x * bpp;
+
+ switch (bpp) {
+ case 1:
+ return *p;
+ break;
+
+ case 2:
+ return *(uint16 *)p;
+ break;
+
+ case 3:
+#ifdef SCUMM_BIG_ENDIAN
+ // if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
+ return p[0] << 16 | p[1] << 8 | p[2];
+#else
+ //else
+ return p[0] | p[1] << 8 | p[2] << 16;
+#endif
+ break;
+
+ case 4:
+ return *(uint32 *)p;
+ break;
+
+ default:
+ return 0; /* shouldn't happen, but avoids warnings */
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::create(int width, int height) {
+ _width = width;
+ _height = height;
+
+ _gameRef->addMem(_width * _height * 4);
+
+ _valid = true;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::isTransparentAt(int x, int y) {
+ return isTransparentAtLite(x, y);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::isTransparentAtLite(int x, int y) {
+ if (x < 0 || x >= _surface->w || y < 0 || y >= _surface->h) {
+ return true;
+ }
+
+ if (_surface->format.bytesPerPixel == 4) {
+ uint32 pixel = *(uint32 *)_surface->getBasePtr(x, y);
+ uint8 r, g, b, a;
+ _surface->format.colorToARGB(pixel, a, r, g, b);
+ if (a <= 128) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::startPixelOp() {
+ //SDL_LockTexture(_texture, NULL, &_lockPixels, &_lockPitch);
+ // Any pixel-op makes the caching useless:
+ BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer);
+ renderer->invalidateTicketsFromSurface(this);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::endPixelOp() {
+ //SDL_UnlockTexture(_texture);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+ return drawSprite(x, y, &rect, 100, 100, 0xFFFFFFFF, true, blendMode, mirrorX, mirrorY);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::displayTrans(int x, int y, Rect32 rect, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+ return drawSprite(x, y, &rect, 100, 100, alpha, false, blendMode, mirrorX, mirrorY);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::displayTransOffset(int x, int y, Rect32 rect, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) {
+ return drawSprite(x, y, &rect, 100, 100, alpha, false, blendMode, mirrorX, mirrorY, offsetX, offsetY);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+ return drawSprite(x, y, &rect, zoomX, zoomY, alpha, false, blendMode, mirrorX, mirrorY);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha, bool transparent, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+ return drawSprite(x, y, &rect, zoomX, zoomY, alpha, !transparent, blendMode, mirrorX, mirrorY);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+ return drawSprite(x, y, &rect, zoomX, zoomY, alpha, false, blendMode, mirrorX, mirrorY);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceOSystem::drawSprite(int x, int y, Rect32 *rect, float zoomX, float zoomY, uint32 alpha, bool alphaDisable, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) {
+ BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer);
+
+ if (!_loaded) {
+ finishLoad();
+ }
+
+ if (renderer->_forceAlphaColor != 0) {
+ alpha = renderer->_forceAlphaColor;
+ }
+
+ byte r = RGBCOLGetR(alpha);
+ byte g = RGBCOLGetG(alpha);
+ byte b = RGBCOLGetB(alpha);
+ byte a = RGBCOLGetA(alpha);
+
+ renderer->setAlphaMod(a);
+ renderer->setColorMod(r, g, b);
+
+#if 0 // These are kept for reference if BlendMode is reimplemented at some point.
+ if (alphaDisable) {
+ SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_NONE);
+ } else {
+ SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_BLEND);
+ }
+#endif
+ // TODO: This _might_ miss the intended behaviour by 1 in each direction
+ // But I think it fits the model used in Wintermute.
+ Common::Rect srcRect;
+ srcRect.left = rect->left;
+ srcRect.top = rect->top;
+ srcRect.setWidth(rect->right - rect->left);
+ srcRect.setHeight(rect->bottom - rect->top);
+
+ Common::Rect position;
+ position.left = x + offsetX;
+ position.top = y + offsetY;
+
+ // Crop off-by-ones:
+ if (position.left == -1) {
+ position.left = 0; // TODO: Something is wrong
+ }
+ if (position.top == -1) {
+ position.top = 0; // TODO: Something is wrong
+ }
+
+ position.setWidth((int16)((float)srcRect.width() * zoomX / 100.f));
+ position.setHeight((int16)((float)srcRect.height() * zoomX / 100.f));
+
+ renderer->modTargetRect(&position);
+
+ /* position.left += offsetX;
+ position.top += offsetY;*/
+
+ // TODO: This actually requires us to have the SAME source-offsets every time,
+ // But no checking is in place for that yet.
+
+ // TODO: Optimize by not doing alpha-blits if we lack or disable alpha
+ bool hasAlpha;
+ if (_hasAlpha && !alphaDisable) {
+ hasAlpha = true;
+ } else {
+ hasAlpha = false;
+ }
+ if (alphaDisable) {
+ warning("BaseSurfaceOSystem::drawSprite - AlphaDisable ignored");
+ }
+
+ renderer->drawSurface(this, _surface, &srcRect, &position, mirrorX, mirrorY, !hasAlpha);
+
+ return STATUS_OK;
+}
+
+bool BaseSurfaceOSystem::putSurface(const Graphics::Surface &surface, bool hasAlpha) {
+ _loaded = true;
+ _surface->free();
+ _surface->copyFrom(surface);
+ _hasAlpha = hasAlpha;
+ BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer);
+ renderer->invalidateTicketsFromSurface(this);
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.h b/engines/wintermute/base/gfx/osystem/base_surface_osystem.h
new file mode 100644
index 0000000000..43422ef4e7
--- /dev/null
+++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.h
@@ -0,0 +1,99 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SURFACESDL_H
+#define WINTERMUTE_BASE_SURFACESDL_H
+
+#include "graphics/surface.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "common/list.h"
+
+namespace Wintermute {
+struct TransparentSurface;
+class BaseImage;
+class BaseSurfaceOSystem : public BaseSurface {
+public:
+ BaseSurfaceOSystem(BaseGame *inGame);
+ ~BaseSurfaceOSystem();
+
+ bool create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime = -1, bool keepLoaded = false);
+ bool create(int width, int height);
+
+ bool isTransparentAt(int x, int y);
+ bool isTransparentAtLite(int x, int y);
+
+ bool startPixelOp();
+ bool endPixelOp();
+
+
+ bool displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false);
+ bool displayTrans(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false);
+ bool displayTransOffset(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false, int offsetX = 0, int offsetY = 0);
+ bool display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false);
+ bool displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, bool transparent = false, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false);
+ bool displayTransform(int x, int y, int hotX, int hotY, Rect32 Rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false);
+ virtual bool putSurface(const Graphics::Surface &surface, bool hasAlpha = false);
+ /* static unsigned DLL_CALLCONV ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle);
+ static int DLL_CALLCONV SeekProc(fi_handle handle, long offset, int origin);
+ static long DLL_CALLCONV TellProc(fi_handle handle);*/
+ virtual int getWidth() {
+ if (!_loaded) {
+ finishLoad();
+ }
+ if (_surface) {
+ return _surface->w;
+ }
+ return _width;
+ }
+ virtual int getHeight() {
+ if (!_loaded) {
+ finishLoad();
+ }
+ if (_surface) {
+ return _surface->h;
+ }
+ return _height;
+ }
+
+private:
+ Graphics::Surface *_surface;
+ bool _loaded;
+ bool finishLoad();
+ bool drawSprite(int x, int y, Rect32 *rect, float zoomX, float zoomY, uint32 alpha, bool alphaDisable, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX = 0, int offsetY = 0);
+ void genAlphaMask(Graphics::Surface *surface);
+ uint32 getPixelAt(Graphics::Surface *surface, int x, int y);
+
+ bool _hasAlpha;
+ void *_lockPixels;
+ int _lockPitch;
+ byte *_alphaMask;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/particles/part_emitter.cpp b/engines/wintermute/base/particles/part_emitter.cpp
new file mode 100644
index 0000000000..bab4d4609e
--- /dev/null
+++ b/engines/wintermute/base/particles/part_emitter.cpp
@@ -0,0 +1,1257 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/particles/part_emitter.h"
+#include "engines/wintermute/base/particles/part_particle.h"
+#include "engines/wintermute/math/vector2.h"
+#include "engines/wintermute/math/matrix4.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+#include "common/math.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(PartEmitter, false)
+
+//////////////////////////////////////////////////////////////////////////
+PartEmitter::PartEmitter(BaseGame *inGame, BaseScriptHolder *owner) : BaseObject(inGame) {
+ _width = _height = 0;
+
+ BasePlatform::setRectEmpty(&_border);
+ _borderThicknessLeft = _borderThicknessRight = _borderThicknessTop = _borderThicknessBottom = 0;
+
+ _angle1 = _angle2 = 0;
+
+ _velocity1 = _velocity2 = 0.0f;
+ _velocityZBased = false;
+
+ _scale1 = _scale2 = 100.0f;
+ _scaleZBased = false;
+
+ _maxParticles = 100;
+
+ _lifeTime1 = _lifeTime2 = 1000;
+ _lifeTimeZBased = false;
+
+ _lastGenTime = 0;
+ _genInterval = 0;
+ _genAmount = 1;
+
+ _overheadTime = 0;
+ _running = false;
+
+ _maxBatches = 0;
+ _batchesGenerated = 0;
+
+ _fadeInTime = _fadeOutTime = 0;
+
+ _alpha1 = _alpha2 = 255;
+ _alphaTimeBased = false;
+
+ _rotation1 = _rotation2 = 0.0f;
+ _angVelocity1 = _angVelocity2 = 0.0f;
+
+ _growthRate1 = _growthRate2 = 0.0f;
+ _exponentialGrowth = false;
+
+ _useRegion = false;
+
+ _emitEvent = NULL;
+ _owner = owner;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+PartEmitter::~PartEmitter(void) {
+ for (uint32 i = 0; i < _particles.size(); i++) {
+ delete _particles[i];
+ }
+ _particles.clear();
+
+ for (uint32 i = 0; i < _forces.size(); i++) {
+ delete _forces[i];
+ }
+ _forces.clear();
+
+
+ for (uint32 i = 0; i < _sprites.size(); i++) {
+ delete[] _sprites[i];
+ }
+ _sprites.clear();
+
+ delete[] _emitEvent;
+ _emitEvent = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::addSprite(const char *filename) {
+ if (!filename) {
+ return STATUS_FAILED;
+ }
+
+ // do we already have the file?
+ for (uint32 i = 0; i < _sprites.size(); i++) {
+ if (scumm_stricmp(filename, _sprites[i]) == 0) {
+ return STATUS_OK;
+ }
+ }
+
+ // check if file exists
+ Common::SeekableReadStream *File = BaseFileManager::getEngineInstance()->openFile(filename);
+ if (!File) {
+ _gameRef->LOG(0, "Sprite '%s' not found", filename);
+ return STATUS_FAILED;
+ } else {
+ BaseFileManager::getEngineInstance()->closeFile(File);
+ }
+
+ char *str = new char[strlen(filename) + 1];
+ strcpy(str, filename);
+ _sprites.add(str);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::removeSprite(const char *filename) {
+ for (uint32 i = 0; i < _sprites.size(); i++) {
+ if (scumm_stricmp(filename, _sprites[i]) == 0) {
+ delete[] _sprites[i];
+ _sprites.remove_at(i);
+ return STATUS_OK;
+ }
+ }
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::initParticle(PartParticle *particle, uint32 currentTime, uint32 timerDelta) {
+ if (!particle) {
+ return STATUS_FAILED;
+ }
+ if (_sprites.size() == 0) {
+ return STATUS_FAILED;
+ }
+
+ int posX = BaseUtils::randomInt(_posX, _posX + _width);
+ int posY = BaseUtils::randomInt(_posY, _posY + _height);
+ float posZ = BaseUtils::randomFloat(0.0f, 100.0f);
+
+ float velocity;
+ if (_velocityZBased) {
+ velocity = _velocity1 + posZ * (_velocity2 - _velocity1) / 100;
+ } else {
+ velocity = BaseUtils::randomFloat(_velocity1, _velocity2);
+ }
+
+ float scale;
+ if (_scaleZBased) {
+ scale = _scale1 + posZ * (_scale2 - _scale1) / 100;
+ } else {
+ scale = BaseUtils::randomFloat(_scale1, _scale2);
+ }
+
+ int lifeTime;
+ if (_lifeTimeZBased) {
+ lifeTime = (int)(_lifeTime2 - posZ * (_lifeTime2 - _lifeTime1) / 100);
+ } else {
+ lifeTime = BaseUtils::randomInt(_lifeTime1, _lifeTime2);
+ }
+
+ float angle = BaseUtils::randomAngle(_angle1, _angle2);
+ int spriteIndex = BaseUtils::randomInt(0, _sprites.size() - 1);
+
+ float rotation = BaseUtils::randomAngle(_rotation1, _rotation2);
+ float angVelocity = BaseUtils::randomFloat(_angVelocity1, _angVelocity2);
+ float growthRate = BaseUtils::randomFloat(_growthRate1, _growthRate2);
+
+ if (!BasePlatform::isRectEmpty(&_border)) {
+ int thicknessLeft = (int)(_borderThicknessLeft - (float)_borderThicknessLeft * posZ / 100.0f);
+ int thicknessRight = (int)(_borderThicknessRight - (float)_borderThicknessRight * posZ / 100.0f);
+ int thicknessTop = (int)(_borderThicknessTop - (float)_borderThicknessTop * posZ / 100.0f);
+ int thicknessBottom = (int)(_borderThicknessBottom - (float)_borderThicknessBottom * posZ / 100.0f);
+
+ particle->_border = _border;
+ particle->_border.left += thicknessLeft;
+ particle->_border.right -= thicknessRight;
+ particle->_border.top += thicknessTop;
+ particle->_border.bottom -= thicknessBottom;
+ }
+
+ Vector2 vecPos((float)posX, (float)posY);
+ Vector2 vecVel(0, velocity);
+
+ Matrix4 matRot;
+ matRot.rotationZ(Common::deg2rad(BaseUtils::normalizeAngle(angle - 180)));
+ matRot.transformVector2(vecVel);
+
+ if (_alphaTimeBased) {
+ particle->_alpha1 = _alpha1;
+ particle->_alpha2 = _alpha2;
+ } else {
+ int alpha = BaseUtils::randomInt(_alpha1, _alpha2);
+ particle->_alpha1 = alpha;
+ particle->_alpha2 = alpha;
+ }
+
+ particle->_creationTime = currentTime;
+ particle->_pos = vecPos;
+ particle->_posZ = posZ;
+ particle->_velocity = vecVel;
+ particle->_scale = scale;
+ particle->_lifeTime = lifeTime;
+ particle->_rotation = rotation;
+ particle->_angVelocity = angVelocity;
+ particle->_growthRate = growthRate;
+ particle->_exponentialGrowth = _exponentialGrowth;
+ particle->_isDead = DID_FAIL(particle->setSprite(_sprites[spriteIndex]));
+ particle->fadeIn(currentTime, _fadeInTime);
+
+
+ if (particle->_isDead) {
+ return STATUS_FAILED;
+ } else {
+ return STATUS_OK;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::update() {
+ if (!_running) {
+ return STATUS_OK;
+ } else {
+ return updateInternal(_gameRef->_timer, _gameRef->_timerDelta);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::updateInternal(uint32 currentTime, uint32 timerDelta) {
+ int numLive = 0;
+
+ for (uint32 i = 0; i < _particles.size(); i++) {
+ _particles[i]->update(this, currentTime, timerDelta);
+
+ if (!_particles[i]->_isDead) {
+ numLive++;
+ }
+ }
+
+
+ // we're understaffed
+ if (numLive < _maxParticles) {
+ bool needsSort = false;
+ if ((int)(currentTime - _lastGenTime) > _genInterval) {
+ _lastGenTime = currentTime;
+ _batchesGenerated++;
+
+ if (_maxBatches > 0 && _batchesGenerated > _maxBatches) {
+ return STATUS_OK;
+ }
+
+ int toGen = MIN(_genAmount, _maxParticles - numLive);
+ while (toGen > 0) {
+ int firstDeadIndex = -1;
+ for (uint32 i = 0; i < _particles.size(); i++) {
+ if (_particles[i]->_isDead) {
+ firstDeadIndex = i;
+ break;
+ }
+ }
+
+ PartParticle *particle;
+ if (firstDeadIndex >= 0) {
+ particle = _particles[firstDeadIndex];
+ } else {
+ particle = new PartParticle(_gameRef);
+ _particles.add(particle);
+ }
+ initParticle(particle, currentTime, timerDelta);
+ needsSort = true;
+
+ toGen--;
+ }
+ }
+ if (needsSort && (_scaleZBased || _velocityZBased || _lifeTimeZBased)) {
+ sortParticlesByZ();
+ }
+
+ // we actually generated some particles and we're not in fast-forward mode
+ if (needsSort && _overheadTime == 0) {
+ if (_owner && _emitEvent) {
+ _owner->applyEvent(_emitEvent);
+ }
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::display(BaseRegion *region) {
+ if (_sprites.size() <= 1) {
+ _gameRef->_renderer->startSpriteBatch();
+ }
+
+ for (uint32 i = 0; i < _particles.size(); i++) {
+ if (region != NULL && _useRegion) {
+ if (!region->pointInRegion((int)_particles[i]->_pos.x, (int)_particles[i]->_pos.y)) {
+ continue;
+ }
+ }
+
+ _particles[i]->display(this);
+ }
+
+ if (_sprites.size() <= 1) {
+ _gameRef->_renderer->endSpriteBatch();
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::start() {
+ for (uint32 i = 0; i < _particles.size(); i++) {
+ _particles[i]->_isDead = true;
+ }
+ _running = true;
+ _batchesGenerated = 0;
+
+
+ if (_overheadTime > 0) {
+ uint32 delta = 500;
+ int steps = _overheadTime / delta;
+ uint32 currentTime = _gameRef->_timer - _overheadTime;
+
+ for (int i = 0; i < steps; i++) {
+ updateInternal(currentTime, delta);
+ currentTime += delta;
+ }
+ _overheadTime = 0;
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::sortParticlesByZ() {
+ // sort particles by _posY
+ Common::sort(_particles.begin(), _particles.end(), PartEmitter::compareZ);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int PartEmitter::compareZ(const void *obj1, const void *obj2) {
+ const PartParticle *p1 = *(const PartParticle *const *)obj1;
+ const PartParticle *p2 = *(const PartParticle *const *)obj2;
+
+ if (p1->_posZ < p2->_posZ) {
+ return -1;
+ } else if (p1->_posZ > p2->_posZ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::setBorder(int x, int y, int width, int height) {
+ BasePlatform::setRect(&_border, x, y, x + width, y + height);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::setBorderThickness(int thicknessLeft, int thicknessRight, int thicknessTop, int thicknessBottom) {
+ _borderThicknessLeft = thicknessLeft;
+ _borderThicknessRight = thicknessRight;
+ _borderThicknessTop = thicknessTop;
+ _borderThicknessBottom = thicknessBottom;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+PartForce *PartEmitter::addForceByName(const Common::String &name) {
+ PartForce *force = NULL;
+
+ for (uint32 i = 0; i < _forces.size(); i++) {
+ if (scumm_stricmp(name.c_str(), _forces[i]->getName()) == 0) {
+ force = _forces[i];
+ break;
+ }
+ }
+ if (!force) {
+ force = new PartForce(_gameRef);
+ if (force) {
+ force->setName(name.c_str());
+ _forces.add(force);
+ }
+ }
+ return force;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::addForce(const Common::String &name, PartForce::TForceType type, int posX, int posY, float angle, float strength) {
+ PartForce *force = addForceByName(name);
+ if (!force) {
+ return STATUS_FAILED;
+ }
+
+ force->_type = type;
+ force->_pos = Vector2(posX, posY);
+
+ force->_direction = Vector2(0, strength);
+ Matrix4 matRot;
+ matRot.rotationZ(Common::deg2rad(BaseUtils::normalizeAngle(angle - 180)));
+ matRot.transformVector2(force->_direction);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::removeForce(const Common::String &name) {
+ for (uint32 i = 0; i < _forces.size(); i++) {
+ if (scumm_stricmp(name.c_str(), _forces[i]->getName()) == 0) {
+ delete _forces[i];
+ _forces.remove_at(i);
+ return STATUS_OK;
+ }
+ }
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetBorder
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetBorder") == 0) {
+ stack->correctParams(4);
+ int borderX = stack->pop()->getInt();
+ int borderY = stack->pop()->getInt();
+ int borderWidth = stack->pop()->getInt();
+ int borderHeight = stack->pop()->getInt();
+
+ stack->pushBool(DID_SUCCEED(setBorder(borderX, borderY, borderWidth, borderHeight)));
+
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetBorderThickness
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetBorderThickness") == 0) {
+ stack->correctParams(4);
+ int left = stack->pop()->getInt();
+ int right = stack->pop()->getInt();
+ int top = stack->pop()->getInt();
+ int bottom = stack->pop()->getInt();
+
+ stack->pushBool(DID_SUCCEED(setBorderThickness(left, right, top, bottom)));
+
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AddSprite
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddSprite") == 0) {
+ stack->correctParams(1);
+ const char *spriteFile = stack->pop()->getString();
+ stack->pushBool(DID_SUCCEED(addSprite(spriteFile)));
+
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // RemoveSprite
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemoveSprite") == 0) {
+ stack->correctParams(1);
+ const char *spriteFile = stack->pop()->getString();
+ stack->pushBool(DID_SUCCEED(removeSprite(spriteFile)));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Start
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Start") == 0) {
+ stack->correctParams(1);
+ _overheadTime = stack->pop()->getInt();
+ stack->pushBool(DID_SUCCEED(start()));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Stop
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Stop") == 0) {
+ stack->correctParams(0);
+
+ for (uint32 i = 0; i < _particles.size(); i++) {
+ delete _particles[i];
+ }
+ _particles.clear();
+
+ _running = false;
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Pause
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Pause") == 0) {
+ stack->correctParams(0);
+ _running = false;
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Resume
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Resume") == 0) {
+ stack->correctParams(0);
+ _running = true;
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddGlobalForce
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddGlobalForce") == 0) {
+ stack->correctParams(3);
+ const char *forceName = stack->pop()->getString();
+ float angle = stack->pop()->getFloat();
+ float strength = stack->pop()->getFloat();
+
+ stack->pushBool(DID_SUCCEED(addForce(forceName, PartForce::FORCE_GLOBAL, 0, 0, angle, strength)));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AddPointForce
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AddPointForce") == 0) {
+ stack->correctParams(5);
+ const char *forceName = stack->pop()->getString();
+ int posX = stack->pop()->getInt();
+ int posY = stack->pop()->getInt();
+ float angle = stack->pop()->getFloat();
+ float strength = stack->pop()->getFloat();
+
+ stack->pushBool(DID_SUCCEED(addForce(forceName, PartForce::FORCE_GLOBAL, posX, posY, angle, strength)));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RemoveForce
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RemoveForce") == 0) {
+ stack->correctParams(1);
+ const char *forceName = stack->pop()->getString();
+
+ stack->pushBool(DID_SUCCEED(removeForce(forceName)));
+
+ return STATUS_OK;
+ } else {
+ return BaseObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *PartEmitter::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("particle-emitter");
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // X
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "X") {
+ _scValue->setInt(_posX);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Y
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Y") {
+ _scValue->setInt(_posY);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Width
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Width") {
+ _scValue->setInt(_width);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Height
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Height") {
+ _scValue->setInt(_height);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Scale1
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Scale1") {
+ _scValue->setFloat(_scale1);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Scale2
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Scale2") {
+ _scValue->setFloat(_scale2);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // ScaleZBased
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ScaleZBased") {
+ _scValue->setBool(_scaleZBased);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Velocity1
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Velocity1") {
+ _scValue->setFloat(_velocity1);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Velocity2
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Velocity2") {
+ _scValue->setFloat(_velocity2);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // VelocityZBased
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "VelocityZBased") {
+ _scValue->setBool(_velocityZBased);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LifeTime1
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "LifeTime1") {
+ _scValue->setInt(_lifeTime1);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // LifeTime2
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "LifeTime2") {
+ _scValue->setInt(_lifeTime2);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // LifeTimeZBased
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "LifeTimeZBased") {
+ _scValue->setBool(_lifeTimeZBased);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Angle1
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Angle1") {
+ _scValue->setInt(_angle1);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Angle2
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Angle2") {
+ _scValue->setInt(_angle2);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AngVelocity1
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AngVelocity1") {
+ _scValue->setFloat(_angVelocity1);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AngVelocity2
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AngVelocity2") {
+ _scValue->setFloat(_angVelocity2);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Rotation1
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Rotation1") {
+ _scValue->setFloat(_rotation1);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Rotation2
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Rotation2") {
+ _scValue->setFloat(_rotation2);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Alpha1
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Alpha1") {
+ _scValue->setInt(_alpha1);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Alpha2
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Alpha2") {
+ _scValue->setInt(_alpha2);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AlphaTimeBased
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AlphaTimeBased") {
+ _scValue->setBool(_alphaTimeBased);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MaxParticles
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MaxParticles") {
+ _scValue->setInt(_maxParticles);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // NumLiveParticles (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumLiveParticles") {
+ int numAlive = 0;
+ for (uint32 i = 0; i < _particles.size(); i++) {
+ if (_particles[i] && !_particles[i]->_isDead) {
+ numAlive++;
+ }
+ }
+ _scValue->setInt(numAlive);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GenerationInterval
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "GenerationInterval") {
+ _scValue->setInt(_genInterval);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GenerationAmount
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "GenerationAmount") {
+ _scValue->setInt(_genAmount);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // MaxBatches
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MaxBatches") {
+ _scValue->setInt(_maxBatches);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FadeInTime
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "FadeInTime") {
+ _scValue->setInt(_fadeInTime);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // FadeOutTime
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "FadeOutTime") {
+ _scValue->setInt(_fadeOutTime);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GrowthRate1
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "GrowthRate1") {
+ _scValue->setFloat(_growthRate1);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GrowthRate2
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "GrowthRate2") {
+ _scValue->setFloat(_growthRate2);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // ExponentialGrowth
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ExponentialGrowth") {
+ _scValue->setBool(_exponentialGrowth);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // UseRegion
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "UseRegion") {
+ _scValue->setBool(_useRegion);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // EmitEvent
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "EmitEvent") {
+ if (!_emitEvent) {
+ _scValue->setNULL();
+ } else {
+ _scValue->setString(_emitEvent);
+ }
+ return _scValue;
+ } else {
+ return BaseObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // X
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "X") == 0) {
+ _posX = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Y
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Y") == 0) {
+ _posY = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Width
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Width") == 0) {
+ _width = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Height
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Height") == 0) {
+ _height = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Scale1
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Scale1") == 0) {
+ _scale1 = value->getFloat();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Scale2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Scale2") == 0) {
+ _scale2 = value->getFloat();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // ScaleZBased
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ScaleZBased") == 0) {
+ _scaleZBased = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Velocity1
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Velocity1") == 0) {
+ _velocity1 = value->getFloat();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Velocity2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Velocity2") == 0) {
+ _velocity2 = value->getFloat();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // VelocityZBased
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "VelocityZBased") == 0) {
+ _velocityZBased = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LifeTime1
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LifeTime1") == 0) {
+ _lifeTime1 = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // LifeTime2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LifeTime2") == 0) {
+ _lifeTime2 = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // LifeTimeZBased
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LifeTimeZBased") == 0) {
+ _lifeTimeZBased = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Angle1
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Angle1") == 0) {
+ _angle1 = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Angle2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Angle2") == 0) {
+ _angle2 = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AngVelocity1
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AngVelocity1") == 0) {
+ _angVelocity1 = value->getFloat();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AngVelocity2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AngVelocity2") == 0) {
+ _angVelocity2 = value->getFloat();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Rotation1
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Rotation1") == 0) {
+ _rotation1 = value->getFloat();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Rotation2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Rotation2") == 0) {
+ _rotation2 = value->getFloat();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Alpha1
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Alpha1") == 0) {
+ _alpha1 = value->getInt();
+ if (_alpha1 < 0) {
+ _alpha1 = 0;
+ }
+ if (_alpha1 > 255) {
+ _alpha1 = 255;
+ }
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Alpha2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Alpha2") == 0) {
+ _alpha2 = value->getInt();
+ if (_alpha2 < 0) {
+ _alpha2 = 0;
+ }
+ if (_alpha2 > 255) {
+ _alpha2 = 255;
+ }
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AlphaTimeBased
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AlphaTimeBased") == 0) {
+ _alphaTimeBased = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MaxParticles
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MaxParticles") == 0) {
+ _maxParticles = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GenerationInterval
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GenerationInterval") == 0) {
+ _genInterval = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GenerationAmount
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GenerationAmount") == 0) {
+ _genAmount = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // MaxBatches
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MaxBatches") == 0) {
+ _maxBatches = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FadeInTime
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FadeInTime") == 0) {
+ _fadeInTime = value->getInt();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // FadeOutTime
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FadeOutTime") == 0) {
+ _fadeOutTime = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GrowthRate1
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GrowthRate1") == 0) {
+ _growthRate1 = value->getFloat();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GrowthRate2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GrowthRate2") == 0) {
+ _growthRate2 = value->getFloat();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // ExponentialGrowth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ExponentialGrowth") == 0) {
+ _exponentialGrowth = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // UseRegion
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "UseRegion") == 0) {
+ _useRegion = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // EmitEvent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "EmitEvent") == 0) {
+ delete[] _emitEvent;
+ _emitEvent = NULL;
+ if (!value->isNULL()) {
+ BaseUtils::setString(&_emitEvent, value->getString());
+ }
+ return STATUS_OK;
+ } else {
+ return BaseObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *PartEmitter::scToString() {
+ return "[particle emitter]";
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool PartEmitter::persist(BasePersistenceManager *persistMgr) {
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_width));
+ persistMgr->transfer(TMEMBER(_height));
+
+ persistMgr->transfer(TMEMBER(_angle1));
+ persistMgr->transfer(TMEMBER(_angle2));
+
+ persistMgr->transfer(TMEMBER(_velocity1));
+ persistMgr->transfer(TMEMBER(_velocity2));
+ persistMgr->transfer(TMEMBER(_velocityZBased));
+
+ persistMgr->transfer(TMEMBER(_scale1));
+ persistMgr->transfer(TMEMBER(_scale2));
+ persistMgr->transfer(TMEMBER(_scaleZBased));
+
+ persistMgr->transfer(TMEMBER(_maxParticles));
+
+ persistMgr->transfer(TMEMBER(_lifeTime1));
+ persistMgr->transfer(TMEMBER(_lifeTime2));
+ persistMgr->transfer(TMEMBER(_lifeTimeZBased));
+
+ persistMgr->transfer(TMEMBER(_genInterval));
+ persistMgr->transfer(TMEMBER(_genAmount));
+
+ persistMgr->transfer(TMEMBER(_running));
+ persistMgr->transfer(TMEMBER(_overheadTime));
+
+ persistMgr->transfer(TMEMBER(_border));
+ persistMgr->transfer(TMEMBER(_borderThicknessLeft));
+ persistMgr->transfer(TMEMBER(_borderThicknessRight));
+ persistMgr->transfer(TMEMBER(_borderThicknessTop));
+ persistMgr->transfer(TMEMBER(_borderThicknessBottom));
+
+ persistMgr->transfer(TMEMBER(_fadeInTime));
+ persistMgr->transfer(TMEMBER(_fadeOutTime));
+
+ persistMgr->transfer(TMEMBER(_alpha1));
+ persistMgr->transfer(TMEMBER(_alpha2));
+ persistMgr->transfer(TMEMBER(_alphaTimeBased));
+
+ persistMgr->transfer(TMEMBER(_angVelocity1));
+ persistMgr->transfer(TMEMBER(_angVelocity2));
+
+ persistMgr->transfer(TMEMBER(_rotation1));
+ persistMgr->transfer(TMEMBER(_rotation2));
+
+ persistMgr->transfer(TMEMBER(_growthRate1));
+ persistMgr->transfer(TMEMBER(_growthRate2));
+ persistMgr->transfer(TMEMBER(_exponentialGrowth));
+
+ persistMgr->transfer(TMEMBER(_useRegion));
+
+ persistMgr->transfer(TMEMBER_INT(_maxBatches));
+ persistMgr->transfer(TMEMBER_INT(_batchesGenerated));
+
+ persistMgr->transfer(TMEMBER(_emitEvent));
+ persistMgr->transfer(TMEMBER(_owner));
+
+
+ _sprites.persist(persistMgr);
+
+ uint32 numForces;
+ if (persistMgr->getIsSaving()) {
+ numForces = _forces.size();
+ persistMgr->transfer(TMEMBER(numForces));
+ for (uint32 i = 0; i < _forces.size(); i++) {
+ _forces[i]->persist(persistMgr);
+ }
+ } else {
+ persistMgr->transfer(TMEMBER(numForces));
+ for (uint32 i = 0; i < numForces; i++) {
+ PartForce *force = new PartForce(_gameRef);
+ force->persist(persistMgr);
+ _forces.add(force);
+ }
+ }
+
+ uint32 numParticles;
+ if (persistMgr->getIsSaving()) {
+ numParticles = _particles.size();
+ persistMgr->transfer(TMEMBER(numParticles));
+ for (uint32 i = 0; i < _particles.size(); i++) {
+ _particles[i]->persist(persistMgr);
+ }
+ } else {
+ persistMgr->transfer(TMEMBER(numParticles));
+ for (uint32 i = 0; i < numParticles; i++) {
+ PartParticle *particle = new PartParticle(_gameRef);
+ particle->persist(persistMgr);
+ _particles.add(particle);
+ }
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/particles/part_emitter.h b/engines/wintermute/base/particles/part_emitter.h
new file mode 100644
index 0000000000..f2c8f139f1
--- /dev/null
+++ b/engines/wintermute/base/particles/part_emitter.h
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_PARTEMITTER_H
+#define WINTERMUTE_PARTEMITTER_H
+
+
+#include "engines/wintermute/base/base_object.h"
+#include "engines/wintermute/base/particles/part_force.h"
+
+namespace Wintermute {
+class BaseRegion;
+class PartParticle;
+class PartEmitter : public BaseObject {
+public:
+ DECLARE_PERSISTENT(PartEmitter, BaseObject)
+
+ PartEmitter(BaseGame *inGame, BaseScriptHolder *Owner);
+ virtual ~PartEmitter(void);
+
+ int _fadeOutTime;
+
+ bool start();
+
+ bool update();
+ bool display() { return display(NULL); } // To avoid shadowing the inherited display-function.
+ bool display(BaseRegion *region);
+
+ bool sortParticlesByZ();
+ bool addSprite(const char *filename);
+ bool removeSprite(const char *filename);
+ bool setBorder(int x, int y, int width, int height);
+ bool setBorderThickness(int thicknessLeft, int thicknessRight, int thicknessTop, int thicknessBottom);
+
+ bool addForce(const Common::String &name, PartForce::TForceType type, int posX, int posY, float angle, float strength);
+ bool removeForce(const Common::String &name);
+
+ BaseArray<PartForce *> _forces;
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+
+
+private:
+ int _width;
+ int _height;
+
+ int _angle1;
+ int _angle2;
+
+ float _rotation1;
+ float _rotation2;
+
+ float _angVelocity1;
+ float _angVelocity2;
+
+ float _growthRate1;
+ float _growthRate2;
+ bool _exponentialGrowth;
+
+ float _velocity1;
+ float _velocity2;
+ bool _velocityZBased;
+
+ float _scale1;
+ float _scale2;
+ bool _scaleZBased;
+
+ int _maxParticles;
+
+ int _lifeTime1;
+ int _lifeTime2;
+ bool _lifeTimeZBased;
+
+ int _genInterval;
+ int _genAmount;
+
+ bool _running;
+ int _overheadTime;
+
+ int _maxBatches;
+ int _batchesGenerated;
+
+ Rect32 _border;
+ int _borderThicknessLeft;
+ int _borderThicknessRight;
+ int _borderThicknessTop;
+ int _borderThicknessBottom;
+
+ int _fadeInTime;
+
+ int _alpha1;
+ int _alpha2;
+ bool _alphaTimeBased;
+
+ bool _useRegion;
+
+ char *_emitEvent;
+ BaseScriptHolder *_owner;
+
+ PartForce *addForceByName(const Common::String &name);
+ int static compareZ(const void *obj1, const void *obj2);
+ bool initParticle(PartParticle *particle, uint32 currentTime, uint32 timerDelta);
+ bool updateInternal(uint32 currentTime, uint32 timerDelta);
+ uint32 _lastGenTime;
+ BaseArray<PartParticle *> _particles;
+ BaseArray<char *> _sprites;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/particles/part_force.cpp b/engines/wintermute/base/particles/part_force.cpp
new file mode 100644
index 0000000000..df84162504
--- /dev/null
+++ b/engines/wintermute/base/particles/part_force.cpp
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/particles/part_force.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+PartForce::PartForce(BaseGame *inGame) : BaseNamedObject(inGame) {
+ _pos = Vector2(0.0f, 0.0f);
+ _direction = Vector2(0.0f, 0.0f);
+ _type = FORCE_POINT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+PartForce::~PartForce(void) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool PartForce::persist(BasePersistenceManager *persistMgr) {
+ if (persistMgr->getIsSaving()) {
+ const char *name = getName();
+ persistMgr->transfer(TMEMBER(name));
+ } else {
+ const char *name;
+ persistMgr->transfer(TMEMBER(name));
+ setName(name);
+ }
+ persistMgr->transfer(TMEMBER(_pos));
+ persistMgr->transfer(TMEMBER(_direction));
+ persistMgr->transfer(TMEMBER_INT(_type));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/particles/part_force.h b/engines/wintermute/base/particles/part_force.h
new file mode 100644
index 0000000000..27f4cb7d90
--- /dev/null
+++ b/engines/wintermute/base/particles/part_force.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_PARTFORCE_H
+#define WINTERMUTE_PARTFORCE_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/base/base_named_object.h"
+#include "engines/wintermute/math/vector2.h"
+
+namespace Wintermute {
+
+class PartForce : public BaseNamedObject {
+public:
+ enum TForceType {
+ FORCE_POINT, FORCE_GLOBAL
+ };
+
+ PartForce(BaseGame *inGame);
+ virtual ~PartForce(void);
+
+ Vector2 _pos;
+ Vector2 _direction;
+ TForceType _type;
+
+ bool persist(BasePersistenceManager *PersistMgr);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/particles/part_particle.cpp b/engines/wintermute/base/particles/part_particle.cpp
new file mode 100644
index 0000000000..0b850d9618
--- /dev/null
+++ b/engines/wintermute/base/particles/part_particle.cpp
@@ -0,0 +1,269 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/particles/part_particle.h"
+#include "engines/wintermute/base/particles/part_emitter.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+PartParticle::PartParticle(BaseGame *inGame) : BaseClass(inGame) {
+ _pos = Vector2(0.0f, 0.0f);
+ _posZ = 0.0f;
+ _velocity = Vector2(0.0f, 0.0f);
+ _scale = 100.0f;
+ _sprite = NULL;
+ _creationTime = 0;
+ _lifeTime = 0;
+ _isDead = true;
+ BasePlatform::setRectEmpty(&_border);
+
+ _state = PARTICLE_NORMAL;
+ _fadeStart = 0;
+ _fadeTime = 0;
+ _currentAlpha = 255;
+
+ _alpha1 = _alpha2 = 255;
+
+ _rotation = 0.0f;
+ _angVelocity = 0.0f;
+
+ _growthRate = 0.0f;
+ _exponentialGrowth = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+PartParticle::~PartParticle(void) {
+ delete _sprite;
+ _sprite = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartParticle::setSprite(const Common::String &filename) {
+ if (_sprite && _sprite->getFilename() && scumm_stricmp(filename.c_str(), _sprite->getFilename()) == 0) {
+ _sprite->reset();
+ return STATUS_OK;
+ }
+
+ delete _sprite;
+ _sprite = NULL;
+
+ SystemClassRegistry::getInstance()->_disabled = true;
+ _sprite = new BaseSprite(_gameRef, (BaseObject*)_gameRef);
+ if (_sprite && DID_SUCCEED(_sprite->loadFile(filename))) {
+ SystemClassRegistry::getInstance()->_disabled = false;
+ return STATUS_OK;
+ } else {
+ delete _sprite;
+ _sprite = NULL;
+ SystemClassRegistry::getInstance()->_disabled = false;
+ return STATUS_FAILED;
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartParticle::update(PartEmitter *emitter, uint32 currentTime, uint32 timerDelta) {
+ if (_state == PARTICLE_FADEIN) {
+ if (currentTime - _fadeStart >= (uint32)_fadeTime) {
+ _state = PARTICLE_NORMAL;
+ _currentAlpha = _alpha1;
+ } else {
+ _currentAlpha = (int)(((float)currentTime - (float)_fadeStart) / (float)_fadeTime * _alpha1);
+ }
+
+ return STATUS_OK;
+ } else if (_state == PARTICLE_FADEOUT) {
+ if (currentTime - _fadeStart >= (uint32)_fadeTime) {
+ _isDead = true;
+ return STATUS_OK;
+ } else {
+ _currentAlpha = _fadeStartAlpha - (int)(((float)currentTime - (float)_fadeStart) / (float)_fadeTime * _fadeStartAlpha);
+ }
+
+ return STATUS_OK;
+ } else {
+ // time is up
+ if (_lifeTime > 0) {
+ if (currentTime - _creationTime >= (uint32)_lifeTime) {
+ if (emitter->_fadeOutTime > 0) {
+ fadeOut(currentTime, emitter->_fadeOutTime);
+ } else {
+ _isDead = true;
+ }
+ }
+ }
+
+ // particle hit the border
+ if (!_isDead && !BasePlatform::isRectEmpty(&_border)) {
+ Point32 p;
+ p.x = (int32)_pos.x;
+ p.y = (int32)_pos.y;
+ if (!BasePlatform::ptInRect(&_border, p)) {
+ fadeOut(currentTime, emitter->_fadeOutTime);
+ }
+ }
+ if (_state != PARTICLE_NORMAL) {
+ return STATUS_OK;
+ }
+
+ // update alpha
+ if (_lifeTime > 0) {
+ int age = (int)(currentTime - _creationTime);
+ int alphaDelta = (int)(_alpha2 - _alpha1);
+
+ _currentAlpha = _alpha1 + (int)(((float)alphaDelta / (float)_lifeTime * (float)age));
+ }
+
+ // update position
+ float elapsedTime = (float)timerDelta / 1000.f;
+
+ for (uint32 i = 0; i < emitter->_forces.size(); i++) {
+ PartForce *force = emitter->_forces[i];
+ switch (force->_type) {
+ case PartForce::FORCE_GLOBAL:
+ _velocity += force->_direction * elapsedTime;
+ break;
+
+ case PartForce::FORCE_POINT: {
+ Vector2 vecDist = force->_pos - _pos;
+ float dist = fabs(vecDist.length());
+
+ dist = 100.0f / dist;
+
+ _velocity += force->_direction * dist * elapsedTime;
+ }
+ break;
+ }
+ }
+ _pos += _velocity * elapsedTime;
+
+ // update rotation
+ _rotation += _angVelocity * elapsedTime;
+ _rotation = BaseUtils::normalizeAngle(_rotation);
+
+ // update scale
+ if (_exponentialGrowth) {
+ _scale += _scale / 100.0f * _growthRate * elapsedTime;
+ } else {
+ _scale += _growthRate * elapsedTime;
+ }
+
+ if (_scale <= 0.0f) {
+ _isDead = true;
+ }
+
+
+ return STATUS_OK;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartParticle::display(PartEmitter *emitter) {
+ if (!_sprite) {
+ return STATUS_FAILED;
+ }
+ if (_isDead) {
+ return STATUS_OK;
+ }
+
+ _sprite->getCurrentFrame();
+ return _sprite->display((int)_pos.x, (int)_pos.y,
+ NULL,
+ _scale, _scale,
+ BYTETORGBA(255, 255, 255, _currentAlpha),
+ _rotation,
+ emitter->_blendMode);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool PartParticle::fadeIn(uint32 currentTime, int fadeTime) {
+ _currentAlpha = 0;
+ _fadeStart = currentTime;
+ _fadeTime = fadeTime;
+ _state = PARTICLE_FADEIN;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartParticle::fadeOut(uint32 currentTime, int fadeTime) {
+ //_currentAlpha = 255;
+ _fadeStartAlpha = _currentAlpha;
+ _fadeStart = currentTime;
+ _fadeTime = fadeTime;
+ _state = PARTICLE_FADEOUT;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool PartParticle::persist(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_alpha1));
+ persistMgr->transfer(TMEMBER(_alpha2));
+ persistMgr->transfer(TMEMBER(_border));
+ persistMgr->transfer(TMEMBER(_pos));
+ persistMgr->transfer(TMEMBER(_posZ));
+ persistMgr->transfer(TMEMBER(_velocity));
+ persistMgr->transfer(TMEMBER(_scale));
+ persistMgr->transfer(TMEMBER(_creationTime));
+ persistMgr->transfer(TMEMBER(_lifeTime));
+ persistMgr->transfer(TMEMBER(_isDead));
+ persistMgr->transfer(TMEMBER_INT(_state));
+ persistMgr->transfer(TMEMBER(_fadeStart));
+ persistMgr->transfer(TMEMBER(_fadeTime));
+ persistMgr->transfer(TMEMBER(_currentAlpha));
+ persistMgr->transfer(TMEMBER(_angVelocity));
+ persistMgr->transfer(TMEMBER(_rotation));
+ persistMgr->transfer(TMEMBER(_growthRate));
+ persistMgr->transfer(TMEMBER(_exponentialGrowth));
+ persistMgr->transfer(TMEMBER(_fadeStartAlpha));
+
+ if (persistMgr->getIsSaving()) {
+ const char *filename = _sprite->getFilename();
+ persistMgr->transfer(TMEMBER(filename));
+ } else {
+ char *filename;
+ persistMgr->transfer(TMEMBER(filename));
+ SystemClassRegistry::getInstance()->_disabled = true;
+ setSprite(filename);
+ SystemClassRegistry::getInstance()->_disabled = false;
+ delete[] filename;
+ filename = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/particles/part_particle.h b/engines/wintermute/base/particles/part_particle.h
new file mode 100644
index 0000000000..4b8c2f131e
--- /dev/null
+++ b/engines/wintermute/base/particles/part_particle.h
@@ -0,0 +1,90 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_PARTPARTICLE_H
+#define WINTERMUTE_PARTPARTICLE_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/math/rect32.h"
+#include "engines/wintermute/math/vector2.h"
+
+namespace Wintermute {
+
+class PartEmitter;
+class BaseSprite;
+class BasePersistenceManager;
+
+class PartParticle : public BaseClass {
+public:
+ enum TParticleState {
+ PARTICLE_NORMAL, PARTICLE_FADEIN, PARTICLE_FADEOUT
+ };
+
+ PartParticle(BaseGame *inGame);
+ virtual ~PartParticle(void);
+
+ float _growthRate;
+ bool _exponentialGrowth;
+
+ float _rotation;
+ float _angVelocity;
+
+ int _alpha1;
+ int _alpha2;
+
+ Rect32 _border;
+ Vector2 _pos;
+ float _posZ;
+ Vector2 _velocity;
+ float _scale;
+ BaseSprite *_sprite;
+ uint32 _creationTime;
+ int _lifeTime;
+ bool _isDead;
+ TParticleState _state;
+
+ bool update(PartEmitter *emitter, uint32 currentTime, uint32 timerDelta);
+ bool display(PartEmitter *emitter);
+
+ bool setSprite(const Common::String &filename);
+
+ bool fadeIn(uint32 currentTime, int fadeTime);
+ bool fadeOut(uint32 currentTime, int fadeTime);
+
+ bool persist(BasePersistenceManager *PersistMgr);
+private:
+ uint32 _fadeStart;
+ int _fadeTime;
+ int _currentAlpha;
+ int _fadeStartAlpha;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/saveload.cpp b/engines/wintermute/base/saveload.cpp
new file mode 100644
index 0000000000..12204e1b35
--- /dev/null
+++ b/engines/wintermute/base/saveload.cpp
@@ -0,0 +1,204 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_persistence_manager.h"
+#include "engines/wintermute/wintermute.h"
+#include "engines/wintermute/base/saveload.h"
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/base_game.h" // Temporary
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "common/savefile.h"
+#include "common/config-manager.h"
+
+namespace Wintermute {
+
+bool SaveLoad::loadGame(const Common::String &filename, BaseGame *gameRef) {
+ gameRef->LOG(0, "Loading game '%s'...", filename.c_str());
+
+ bool ret;
+
+ gameRef->_renderer->initSaveLoad(false);
+
+ gameRef->_loadInProgress = true;
+ BasePersistenceManager *pm = new BasePersistenceManager();
+ if (DID_SUCCEED(ret = pm->initLoad(filename))) {
+ //if (DID_SUCCEED(ret = cleanup())) {
+ if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->loadTable(gameRef, pm))) {
+ if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->loadInstances(gameRef, pm))) {
+ // Restore random-seed:
+ BaseEngine::instance().getRandomSource()->setSeed(pm->getDWORD());
+
+ // data initialization after load
+ SaveLoad::initAfterLoad();
+
+ gameRef->applyEvent("AfterLoad", true);
+
+ gameRef->displayContent(true, false);
+ //_renderer->flip();
+ }
+ }
+ }
+
+ delete pm;
+ gameRef->_loadInProgress = false;
+
+ gameRef->_renderer->endSaveLoad();
+
+ //_gameRef->LOG(0, "Load end %d", BaseUtils::GetUsedMemMB());
+ // AdGame:
+ if (DID_SUCCEED(ret)) {
+ SystemClassRegistry::getInstance()->enumInstances(SaveLoad::afterLoadRegion, "AdRegion", NULL);
+ }
+ return ret;
+}
+
+bool SaveLoad::saveGame(int slot, const char *desc, bool quickSave, BaseGame *gameRef) {
+ Common::String filename = SaveLoad::getSaveSlotFilename(slot);
+
+ gameRef->LOG(0, "Saving game '%s'...", filename.c_str());
+
+ gameRef->applyEvent("BeforeSave", true);
+
+ bool ret;
+
+ BasePersistenceManager *pm = new BasePersistenceManager();
+ if (DID_SUCCEED(ret = pm->initSave(desc))) {
+ gameRef->_renderer->initSaveLoad(true, quickSave); // TODO: The original code inited the indicator before the conditionals
+ if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->saveTable(gameRef, pm, quickSave))) {
+ if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->saveInstances(gameRef, pm, quickSave))) {
+ pm->putDWORD(BaseEngine::instance().getRandomSource()->getSeed());
+ if (DID_SUCCEED(ret = pm->saveFile(filename))) {
+ ConfMan.setInt("most_recent_saveslot", slot);
+ }
+ }
+ }
+ }
+
+ delete pm;
+
+ gameRef->_renderer->endSaveLoad();
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SaveLoad::initAfterLoad() {
+ SystemClassRegistry::getInstance()->enumInstances(afterLoadRegion, "BaseRegion", NULL);
+ SystemClassRegistry::getInstance()->enumInstances(afterLoadSubFrame, "BaseSubFrame", NULL);
+ SystemClassRegistry::getInstance()->enumInstances(afterLoadSound, "BaseSound", NULL);
+ SystemClassRegistry::getInstance()->enumInstances(afterLoadFont, "BaseFontTT", NULL);
+ SystemClassRegistry::getInstance()->enumInstances(afterLoadScript, "ScScript", NULL);
+ // AdGame:
+ SystemClassRegistry::getInstance()->enumInstances(afterLoadScene, "AdScene", NULL);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::afterLoadScene(void *scene, void *data) {
+ ((AdScene *)scene)->afterLoad();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::afterLoadRegion(void *region, void *data) {
+ ((BaseRegion *)region)->createRegion();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::afterLoadSubFrame(void *subframe, void *data) {
+ ((BaseSubFrame *)subframe)->setSurfaceSimple();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::afterLoadSound(void *sound, void *data) {
+ ((BaseSound *)sound)->setSoundSimple();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::afterLoadFont(void *font, void *data) {
+ ((BaseFont *)font)->afterLoad();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::afterLoadScript(void *script, void *data) {
+ ((ScScript *)script)->afterLoad();
+}
+
+Common::String SaveLoad::getSaveSlotFilename(int slot) {
+ BasePersistenceManager *pm = new BasePersistenceManager();
+ Common::String filename = pm->getFilenameForSlot(slot);
+ delete pm;
+ debugC(kWintermuteDebugSaveGame, "getSaveSlotFileName(%d) = %s", slot, filename.c_str());
+ return filename;
+}
+
+bool SaveLoad::getSaveSlotDescription(int slot, char *buffer) {
+ buffer[0] = '\0';
+
+ Common::String filename = getSaveSlotFilename(slot);
+ BasePersistenceManager *pm = new BasePersistenceManager();
+ if (!pm) {
+ return false;
+ }
+
+ if (!(pm->initLoad(filename))) {
+ delete pm;
+ return false;
+ }
+
+ strcpy(buffer, pm->_savedDescription);
+ delete pm;
+
+ return true;
+}
+
+bool SaveLoad::isSaveSlotUsed(int slot) {
+ Common::String filename = getSaveSlotFilename(slot);
+ BasePersistenceManager *pm = new BasePersistenceManager();
+ bool ret = pm->getSaveExists(slot);
+ delete pm;
+ return ret;
+}
+
+bool SaveLoad::emptySaveSlot(int slot) {
+ Common::String filename = getSaveSlotFilename(slot);
+ BasePersistenceManager *pm = new BasePersistenceManager();
+ ((WintermuteEngine *)g_engine)->getSaveFileMan()->removeSavefile(pm->getFilenameForSlot(slot));
+ delete pm;
+ return true;
+}
+
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/saveload.h b/engines/wintermute/base/saveload.h
new file mode 100644
index 0000000000..722f7a89b6
--- /dev/null
+++ b/engines/wintermute/base/saveload.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SAVEGAME_H
+#define WINTERMUTE_SAVEGAME_H
+
+#include "common/str.h"
+
+namespace Wintermute {
+class BaseGame;
+class SaveLoad {
+public:
+ static bool emptySaveSlot(int slot);
+ static bool isSaveSlotUsed(int slot);
+ static bool getSaveSlotDescription(int slot, char *buffer);
+ static Common::String getSaveSlotFilename(int slot);
+
+ static bool loadGame(const Common::String &filename, BaseGame *gameRef);
+ static bool saveGame(int slot, const char *desc, bool quickSave, BaseGame *gameRef);
+ static bool initAfterLoad();
+ static void afterLoadScene(void *scene, void *data);
+ static void afterLoadRegion(void *region, void *data);
+private:
+ static void afterLoadSubFrame(void *subframe, void *data);
+ static void afterLoadSound(void *sound, void *data);
+ static void afterLoadFont(void *font, void *data);
+ static void afterLoadScript(void *script, void *data);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/dcscript.h b/engines/wintermute/base/scriptables/dcscript.h
new file mode 100644
index 0000000000..4aae897dc2
--- /dev/null
+++ b/engines/wintermute/base/scriptables/dcscript.h
@@ -0,0 +1,141 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_DCSCRIPT_H
+#define WINTERMUTE_DCSCRIPT_H
+
+namespace Wintermute {
+
+#define SCRIPT_MAGIC 0xDEC0ADDE
+#define SCRIPT_VERSION 0x0102
+
+// value types
+typedef enum {
+ VAL_NULL,
+ VAL_STRING,
+ VAL_INT,
+ VAL_BOOL,
+ VAL_FLOAT,
+ VAL_OBJECT,
+ VAL_NATIVE,
+ VAL_VARIABLE_REF
+} TValType;
+
+
+// script states
+typedef enum {
+ SCRIPT_RUNNING,
+ SCRIPT_WAITING,
+ SCRIPT_SLEEPING,
+ SCRIPT_FINISHED,
+ SCRIPT_PERSISTENT,
+ SCRIPT_ERROR,
+ SCRIPT_PAUSED,
+ SCRIPT_WAITING_SCRIPT,
+ SCRIPT_THREAD_FINISHED
+} TScriptState;
+
+// opcodes
+typedef enum {
+ II_DEF_VAR = 0,
+ II_DEF_GLOB_VAR,
+ II_RET,
+ II_RET_EVENT,
+ II_CALL,
+ II_CALL_BY_EXP,
+ II_EXTERNAL_CALL,
+ II_SCOPE,
+ II_CORRECT_STACK,
+ II_CREATE_OBJECT,
+ II_POP_EMPTY,
+ II_PUSH_VAR,
+ II_PUSH_VAR_REF,
+ II_POP_VAR,
+ II_PUSH_VAR_THIS, // push current this on stack
+ II_PUSH_INT,
+ II_PUSH_BOOL,
+ II_PUSH_FLOAT,
+ II_PUSH_STRING,
+ II_PUSH_NULL,
+ II_PUSH_THIS_FROM_STACK,
+ II_PUSH_THIS,
+ II_POP_THIS,
+ II_PUSH_BY_EXP,
+ II_POP_BY_EXP,
+ II_JMP,
+ II_JMP_FALSE,
+ II_ADD,
+ II_SUB,
+ II_MUL,
+ II_DIV,
+ II_MODULO,
+ II_NOT,
+ II_AND,
+ II_OR,
+ II_CMP_EQ,
+ II_CMP_NE,
+ II_CMP_L,
+ II_CMP_G,
+ II_CMP_LE,
+ II_CMP_GE,
+ II_CMP_STRICT_EQ,
+ II_CMP_STRICT_NE,
+ II_DBG_LINE,
+ II_POP_REG1,
+ II_PUSH_REG1,
+ II_DEF_CONST_VAR
+} TInstruction;
+
+// external data types
+typedef enum {
+ TYPE_VOID = 0,
+ TYPE_BOOL,
+ TYPE_LONG,
+ TYPE_BYTE,
+ TYPE_STRING,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_MEMBUFFER
+} TExternalType;
+
+
+// call types
+typedef enum {
+ CALL_STDCALL = 0,
+ CALL_CDECL,
+ CALL_THISCALL
+} TCallType;
+
+// element types
+typedef enum {
+ ELEMENT_STRING = 0
+} TElementType;
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script.cpp b/engines/wintermute/base/scriptables/script.cpp
new file mode 100644
index 0000000000..9469bd46a7
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script.cpp
@@ -0,0 +1,1467 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/scriptables/script_engine.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "common/memstream.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(ScScript, false)
+
+//////////////////////////////////////////////////////////////////////////
+ScScript::ScScript(BaseGame *inGame, ScEngine *engine) : BaseClass(inGame) {
+ _buffer = NULL;
+ _bufferSize = _iP = 0;
+ _scriptStream = NULL;
+ _filename = NULL;
+ _currentLine = 0;
+
+ _symbols = NULL;
+ _numSymbols = 0;
+
+ _engine = engine;
+
+ _globals = NULL;
+
+ _scopeStack = NULL;
+ _callStack = NULL;
+ _thisStack = NULL;
+ _stack = NULL;
+
+ _operand = NULL;
+ _reg1 = NULL;
+
+ _functions = NULL;
+ _numFunctions = 0;
+
+ _methods = NULL;
+ _numMethods = 0;
+
+ _events = NULL;
+ _numEvents = 0;
+
+ _externals = NULL;
+ _numExternals = 0;
+
+ _state = SCRIPT_FINISHED;
+ _origState = SCRIPT_FINISHED;
+
+ _waitObject = NULL;
+ _waitTime = 0;
+ _waitFrozen = false;
+ _waitScript = NULL;
+
+ _timeSlice = 0;
+
+ _thread = false;
+ _methodThread = false;
+ _threadEvent = NULL;
+
+ _freezable = true;
+ _owner = NULL;
+
+ _unbreakable = false;
+ _parentScript = NULL;
+
+ _tracingMode = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScScript::~ScScript() {
+ cleanup();
+}
+
+void ScScript::readHeader() {
+ uint32 oldPos = _scriptStream->pos();
+ _scriptStream->seek(0);
+ _header.magic = _scriptStream->readUint32LE();
+ _header.version = _scriptStream->readUint32LE();
+ _header.codeStart = _scriptStream->readUint32LE();
+ _header.funcTable = _scriptStream->readUint32LE();
+ _header.symbolTable = _scriptStream->readUint32LE();
+ _header.eventTable = _scriptStream->readUint32LE();
+ _header.externalsTable = _scriptStream->readUint32LE();
+ _header.methodTable = _scriptStream->readUint32LE();
+ _scriptStream->seek(oldPos);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::initScript() {
+ if (!_scriptStream) {
+ _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize);
+ }
+ readHeader();
+
+ if (_header.magic != SCRIPT_MAGIC) {
+ _gameRef->LOG(0, "File '%s' is not a valid compiled script", _filename);
+ cleanup();
+ return STATUS_FAILED;
+ }
+
+ if (_header.version > SCRIPT_VERSION) {
+ _gameRef->LOG(0, "Script '%s' has a wrong version %d.%d (expected %d.%d)", _filename, _header.version / 256, _header.version % 256, SCRIPT_VERSION / 256, SCRIPT_VERSION % 256);
+ cleanup();
+ return STATUS_FAILED;
+ }
+
+ initTables();
+
+ // init stacks
+ _scopeStack = new ScStack(_gameRef);
+ _callStack = new ScStack(_gameRef);
+ _thisStack = new ScStack(_gameRef);
+ _stack = new ScStack(_gameRef);
+
+ _operand = new ScValue(_gameRef);
+ _reg1 = new ScValue(_gameRef);
+
+
+ // skip to the beginning
+ _iP = _header.codeStart;
+ _scriptStream->seek(_iP);
+ _currentLine = 0;
+
+ // ready to rumble...
+ _state = SCRIPT_RUNNING;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::initTables() {
+ uint32 origIP = _iP;
+
+ readHeader();
+ // load symbol table
+ _iP = _header.symbolTable;
+
+ _numSymbols = getDWORD();
+ _symbols = new char*[_numSymbols];
+ for (uint32 i = 0; i < _numSymbols; i++) {
+ uint32 index = getDWORD();
+ _symbols[index] = getString();
+ }
+
+ // load functions table
+ _iP = _header.funcTable;
+
+ _numFunctions = getDWORD();
+ _functions = new TFunctionPos[_numFunctions];
+ for (uint32 i = 0; i < _numFunctions; i++) {
+ _functions[i].pos = getDWORD();
+ _functions[i].name = getString();
+ }
+
+
+ // load events table
+ _iP = _header.eventTable;
+
+ _numEvents = getDWORD();
+ _events = new TEventPos[_numEvents];
+ for (uint32 i = 0; i < _numEvents; i++) {
+ _events[i].pos = getDWORD();
+ _events[i].name = getString();
+ }
+
+
+ // load externals
+ if (_header.version >= 0x0101) {
+ _iP = _header.externalsTable;
+
+ _numExternals = getDWORD();
+ _externals = new TExternalFunction[_numExternals];
+ for (uint32 i = 0; i < _numExternals; i++) {
+ _externals[i].dll_name = getString();
+ _externals[i].name = getString();
+ _externals[i].call_type = (TCallType)getDWORD();
+ _externals[i].returns = (TExternalType)getDWORD();
+ _externals[i].nu_params = getDWORD();
+ if (_externals[i].nu_params > 0) {
+ _externals[i].params = new TExternalType[_externals[i].nu_params];
+ for (int j = 0; j < _externals[i].nu_params; j++) {
+ _externals[i].params[j] = (TExternalType)getDWORD();
+ }
+ }
+ }
+ }
+
+ // load method table
+ _iP = _header.methodTable;
+
+ _numMethods = getDWORD();
+ _methods = new TMethodPos[_numMethods];
+ for (uint32 i = 0; i < _numMethods; i++) {
+ _methods[i].pos = getDWORD();
+ _methods[i].name = getString();
+ }
+
+
+ _iP = origIP;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::create(const char *filename, byte *buffer, uint32 size, BaseScriptHolder *owner) {
+ cleanup();
+
+ _thread = false;
+ _methodThread = false;
+
+ delete[] _threadEvent;
+ _threadEvent = NULL;
+
+ _filename = new char[strlen(filename) + 1];
+ if (_filename) {
+ strcpy(_filename, filename);
+ }
+
+ _buffer = new byte [size];
+ if (!_buffer) {
+ return STATUS_FAILED;
+ }
+
+ memcpy(_buffer, buffer, size);
+
+ _bufferSize = size;
+
+ bool res = initScript();
+ if (DID_FAIL(res)) {
+ return res;
+ }
+
+ // establish global variables table
+ _globals = new ScValue(_gameRef);
+
+ _owner = owner;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::createThread(ScScript *original, uint32 initIP, const Common::String &eventName) {
+ cleanup();
+
+ _thread = true;
+ _methodThread = false;
+ _threadEvent = new char[eventName.size() + 1];
+ if (_threadEvent) {
+ strcpy(_threadEvent, eventName.c_str());
+ }
+
+ // copy filename
+ _filename = new char[strlen(original->_filename) + 1];
+ if (_filename) {
+ strcpy(_filename, original->_filename);
+ }
+
+ // copy buffer
+ _buffer = new byte [original->_bufferSize];
+ if (!_buffer) {
+ return STATUS_FAILED;
+ }
+
+ memcpy(_buffer, original->_buffer, original->_bufferSize);
+ _bufferSize = original->_bufferSize;
+
+ // initialize
+ bool res = initScript();
+ if (DID_FAIL(res)) {
+ return res;
+ }
+
+ // copy globals
+ _globals = original->_globals;
+
+ // skip to the beginning of the event
+ _iP = initIP;
+ _scriptStream->seek(_iP);
+
+ _timeSlice = original->_timeSlice;
+ _freezable = original->_freezable;
+ _owner = original->_owner;
+
+ _engine = original->_engine;
+ _parentScript = original;
+
+ return STATUS_OK;
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::createMethodThread(ScScript *original, const Common::String &methodName) {
+ uint32 ip = original->getMethodPos(methodName);
+ if (ip == 0) {
+ return STATUS_FAILED;
+ }
+
+ cleanup();
+
+ _thread = true;
+ _methodThread = true;
+ _threadEvent = new char[methodName.size() + 1];
+ if (_threadEvent) {
+ strcpy(_threadEvent, methodName.c_str());
+ }
+
+ // copy filename
+ _filename = new char[strlen(original->_filename) + 1];
+ if (_filename) {
+ strcpy(_filename, original->_filename);
+ }
+
+ // copy buffer
+ _buffer = new byte [original->_bufferSize];
+ if (!_buffer) {
+ return STATUS_FAILED;
+ }
+
+ memcpy(_buffer, original->_buffer, original->_bufferSize);
+ _bufferSize = original->_bufferSize;
+
+ // initialize
+ bool res = initScript();
+ if (DID_FAIL(res)) {
+ return res;
+ }
+
+ // copy globals
+ _globals = original->_globals;
+
+ // skip to the beginning of the event
+ _iP = ip;
+
+ _timeSlice = original->_timeSlice;
+ _freezable = original->_freezable;
+ _owner = original->_owner;
+
+ _engine = original->_engine;
+ _parentScript = original;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScScript::cleanup() {
+ if (_buffer) {
+ delete[] _buffer;
+ }
+ _buffer = NULL;
+
+ if (_filename) {
+ delete[] _filename;
+ }
+ _filename = NULL;
+
+ if (_symbols) {
+ delete[] _symbols;
+ }
+ _symbols = NULL;
+ _numSymbols = 0;
+
+ if (_globals && !_thread) {
+ delete _globals;
+ }
+ _globals = NULL;
+
+ delete _scopeStack;
+ _scopeStack = NULL;
+
+ delete _callStack;
+ _callStack = NULL;
+
+ delete _thisStack;
+ _thisStack = NULL;
+
+ delete _stack;
+ _stack = NULL;
+
+ if (_functions) {
+ delete[] _functions;
+ }
+ _functions = NULL;
+ _numFunctions = 0;
+
+ if (_methods) {
+ delete[] _methods;
+ }
+ _methods = NULL;
+ _numMethods = 0;
+
+ if (_events) {
+ delete[] _events;
+ }
+ _events = NULL;
+ _numEvents = 0;
+
+
+ if (_externals) {
+ for (uint32 i = 0; i < _numExternals; i++) {
+ if (_externals[i].nu_params > 0) {
+ delete[] _externals[i].params;
+ }
+ }
+ delete[] _externals;
+ }
+ _externals = NULL;
+ _numExternals = 0;
+
+ delete _operand;
+ delete _reg1;
+ _operand = NULL;
+ _reg1 = NULL;
+
+ delete[] _threadEvent;
+ _threadEvent = NULL;
+
+ _state = SCRIPT_FINISHED;
+
+ _waitObject = NULL;
+ _waitTime = 0;
+ _waitFrozen = false;
+ _waitScript = NULL;
+
+ _parentScript = NULL; // ref only
+
+ delete _scriptStream;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 ScScript::getDWORD() {
+ _scriptStream->seek((int32)_iP);
+ uint32 ret = _scriptStream->readUint32LE();
+ _iP += sizeof(uint32);
+// assert(oldRet == ret);
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+double ScScript::getFloat() {
+ _scriptStream->seek((int32)_iP);
+ byte buffer[8];
+ _scriptStream->read(buffer, 8);
+
+#ifdef SCUMM_BIG_ENDIAN
+ // TODO: For lack of a READ_LE_UINT64
+ SWAP(buffer[0], buffer[7]);
+ SWAP(buffer[1], buffer[6]);
+ SWAP(buffer[2], buffer[5]);
+ SWAP(buffer[3], buffer[4]);
+#endif
+
+ double ret = *(double *)(buffer);
+ _iP += 8; // Hardcode the double-size used originally.
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *ScScript::getString() {
+ char *ret = (char *)(_buffer + _iP);
+ while (*(char *)(_buffer + _iP) != '\0') {
+ _iP++;
+ }
+ _iP++; // string terminator
+ _scriptStream->seek(_iP);
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::executeInstruction() {
+ bool ret = STATUS_OK;
+
+ uint32 dw;
+ const char *str = NULL;
+
+ //ScValue* op = new ScValue(_gameRef);
+ _operand->cleanup();
+
+ ScValue *op1;
+ ScValue *op2;
+
+ uint32 inst = getDWORD();
+ switch (inst) {
+
+ case II_DEF_VAR:
+ _operand->setNULL();
+ dw = getDWORD();
+ if (_scopeStack->_sP < 0) {
+ _globals->setProp(_symbols[dw], _operand);
+ } else {
+ _scopeStack->getTop()->setProp(_symbols[dw], _operand);
+ }
+
+ break;
+
+ case II_DEF_GLOB_VAR:
+ case II_DEF_CONST_VAR: {
+ dw = getDWORD();
+ /* char *temp = _symbols[dw]; // TODO delete */
+ // only create global var if it doesn't exist
+ if (!_engine->_globals->propExists(_symbols[dw])) {
+ _operand->setNULL();
+ _engine->_globals->setProp(_symbols[dw], _operand, false, inst == II_DEF_CONST_VAR);
+ }
+ break;
+ }
+
+ case II_RET:
+ if (_scopeStack->_sP >= 0 && _callStack->_sP >= 0) {
+ _scopeStack->pop();
+ _iP = (uint32)_callStack->pop()->getInt();
+ } else {
+ if (_thread) {
+ _state = SCRIPT_THREAD_FINISHED;
+ } else {
+ if (_numEvents == 0 && _numMethods == 0) {
+ _state = SCRIPT_FINISHED;
+ } else {
+ _state = SCRIPT_PERSISTENT;
+ }
+ }
+ }
+
+ break;
+
+ case II_RET_EVENT:
+ _state = SCRIPT_FINISHED;
+ break;
+
+
+ case II_CALL:
+ dw = getDWORD();
+
+ _operand->setInt(_iP);
+ _callStack->push(_operand);
+
+ _iP = dw;
+
+ break;
+
+ case II_CALL_BY_EXP: {
+ // push var
+ // push string
+ str = _stack->pop()->getString();
+ char *methodName = new char[strlen(str) + 1];
+ strcpy(methodName, str);
+
+ ScValue *var = _stack->pop();
+ if (var->_type == VAL_VARIABLE_REF) {
+ var = var->_valRef;
+ }
+
+ bool res = STATUS_FAILED;
+ bool triedNative = false;
+
+ // we are already calling this method, try native
+ if (_thread && _methodThread && strcmp(methodName, _threadEvent) == 0 && var->_type == VAL_NATIVE && _owner == var->getNative()) {
+ triedNative = true;
+ res = var->_valNative->scCallMethod(this, _stack, _thisStack, methodName);
+ }
+
+ if (DID_FAIL(res)) {
+ if (var->isNative() && var->getNative()->canHandleMethod(methodName)) {
+ if (!_unbreakable) {
+ _waitScript = var->getNative()->invokeMethodThread(methodName);
+ if (!_waitScript) {
+ _stack->correctParams(0);
+ runtimeError("Error invoking method '%s'.", methodName);
+ _stack->pushNULL();
+ } else {
+ _state = SCRIPT_WAITING_SCRIPT;
+ _waitScript->copyParameters(_stack);
+ }
+ } else {
+ // can call methods in unbreakable mode
+ _stack->correctParams(0);
+ runtimeError("Cannot call method '%s'. Ignored.", methodName);
+ _stack->pushNULL();
+ }
+ delete[] methodName;
+ break;
+ }
+ /*
+ ScValue* val = var->getProp(MethodName);
+ if (val){
+ dw = GetFuncPos(val->getString());
+ if (dw==0){
+ TExternalFunction* f = GetExternal(val->getString());
+ if (f){
+ ExternalCall(_stack, _thisStack, f);
+ }
+ else{
+ // not an internal nor external, try for native function
+ _gameRef->ExternalCall(this, _stack, _thisStack, val->getString());
+ }
+ }
+ else{
+ _operand->setInt(_iP);
+ _callStack->Push(_operand);
+ _iP = dw;
+ }
+ }
+ */
+ else {
+ res = STATUS_FAILED;
+ if (var->_type == VAL_NATIVE && !triedNative) {
+ res = var->_valNative->scCallMethod(this, _stack, _thisStack, methodName);
+ }
+
+ if (DID_FAIL(res)) {
+ _stack->correctParams(0);
+ runtimeError("Call to undefined method '%s'. Ignored.", methodName);
+ _stack->pushNULL();
+ }
+ }
+ }
+ delete[] methodName;
+ }
+ break;
+
+ case II_EXTERNAL_CALL: {
+ uint32 symbolIndex = getDWORD();
+
+ TExternalFunction *f = getExternal(_symbols[symbolIndex]);
+ if (f) {
+ externalCall(_stack, _thisStack, f);
+ } else {
+ _gameRef->externalCall(this, _stack, _thisStack, _symbols[symbolIndex]);
+ }
+
+ break;
+ }
+ case II_SCOPE:
+ _operand->setNULL();
+ _scopeStack->push(_operand);
+ break;
+
+ case II_CORRECT_STACK:
+ dw = getDWORD(); // params expected
+ _stack->correctParams(dw);
+ break;
+
+ case II_CREATE_OBJECT:
+ _operand->setObject();
+ _stack->push(_operand);
+ break;
+
+ case II_POP_EMPTY:
+ _stack->pop();
+ break;
+
+ case II_PUSH_VAR: {
+ ScValue *var = getVar(_symbols[getDWORD()]);
+ if (false && /*var->_type==VAL_OBJECT ||*/ var->_type == VAL_NATIVE) {
+ _operand->setReference(var);
+ _stack->push(_operand);
+ } else {
+ _stack->push(var);
+ }
+ break;
+ }
+
+ case II_PUSH_VAR_REF: {
+ ScValue *var = getVar(_symbols[getDWORD()]);
+ _operand->setReference(var);
+ _stack->push(_operand);
+ break;
+ }
+
+ case II_POP_VAR: {
+ char *varName = _symbols[getDWORD()];
+ ScValue *var = getVar(varName);
+ if (var) {
+ ScValue *val = _stack->pop();
+ if (!val) {
+ runtimeError("Script stack corruption detected. Please report this script at WME bug reports forum.");
+ var->setNULL();
+ } else {
+ if (val->getType() == VAL_VARIABLE_REF) {
+ val = val->_valRef;
+ }
+ if (val->_type == VAL_NATIVE) {
+ var->setValue(val);
+ } else {
+ var->copy(val);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case II_PUSH_VAR_THIS:
+ _stack->push(_thisStack->getTop());
+ break;
+
+ case II_PUSH_INT:
+ _stack->pushInt((int)getDWORD());
+ break;
+
+ case II_PUSH_FLOAT:
+ _stack->pushFloat(getFloat());
+ break;
+
+
+ case II_PUSH_BOOL:
+ _stack->pushBool(getDWORD() != 0);
+
+ break;
+
+ case II_PUSH_STRING:
+ _stack->pushString(getString());
+ break;
+
+ case II_PUSH_NULL:
+ _stack->pushNULL();
+ break;
+
+ case II_PUSH_THIS_FROM_STACK:
+ _operand->setReference(_stack->getTop());
+ _thisStack->push(_operand);
+ break;
+
+ case II_PUSH_THIS:
+ _operand->setReference(getVar(_symbols[getDWORD()]));
+ _thisStack->push(_operand);
+ break;
+
+ case II_POP_THIS:
+ _thisStack->pop();
+ break;
+
+ case II_PUSH_BY_EXP: {
+ str = _stack->pop()->getString();
+ ScValue *val = _stack->pop()->getProp(str);
+ if (val) {
+ _stack->push(val);
+ } else {
+ _stack->pushNULL();
+ }
+
+ break;
+ }
+
+ case II_POP_BY_EXP: {
+ str = _stack->pop()->getString();
+ ScValue *var = _stack->pop();
+ ScValue *val = _stack->pop();
+
+ if (val == NULL) {
+ runtimeError("Script stack corruption detected. Please report this script at WME bug reports forum.");
+ var->setNULL();
+ } else {
+ var->setProp(str, val);
+ }
+
+ break;
+ }
+
+ case II_PUSH_REG1:
+ _stack->push(_reg1);
+ break;
+
+ case II_POP_REG1:
+ _reg1->copy(_stack->pop());
+ break;
+
+ case II_JMP:
+ _iP = getDWORD();
+ break;
+
+ case II_JMP_FALSE: {
+ dw = getDWORD();
+ //if (!_stack->pop()->getBool()) _iP = dw;
+ ScValue *val = _stack->pop();
+ if (!val) {
+ runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ } else {
+ if (!val->getBool()) {
+ _iP = dw;
+ }
+ }
+ break;
+ }
+
+ case II_ADD:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op1->isNULL() || op2->isNULL()) {
+ _operand->setNULL();
+ } else if (op1->getType() == VAL_STRING || op2->getType() == VAL_STRING) {
+ char *tempStr = new char [strlen(op1->getString()) + strlen(op2->getString()) + 1];
+ strcpy(tempStr, op1->getString());
+ strcat(tempStr, op2->getString());
+ _operand->setString(tempStr);
+ delete[] tempStr;
+ } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) {
+ _operand->setInt(op1->getInt() + op2->getInt());
+ } else {
+ _operand->setFloat(op1->getFloat() + op2->getFloat());
+ }
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_SUB:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op1->isNULL() || op2->isNULL()) {
+ _operand->setNULL();
+ } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) {
+ _operand->setInt(op1->getInt() - op2->getInt());
+ } else {
+ _operand->setFloat(op1->getFloat() - op2->getFloat());
+ }
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_MUL:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op1->isNULL() || op2->isNULL()) {
+ _operand->setNULL();
+ } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) {
+ _operand->setInt(op1->getInt() * op2->getInt());
+ } else {
+ _operand->setFloat(op1->getFloat() * op2->getFloat());
+ }
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_DIV:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op2->getFloat() == 0.0f) {
+ runtimeError("Division by zero.");
+ }
+
+ if (op1->isNULL() || op2->isNULL() || op2->getFloat() == 0.0f) {
+ _operand->setNULL();
+ } else {
+ _operand->setFloat(op1->getFloat() / op2->getFloat());
+ }
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_MODULO:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op2->getInt() == 0) {
+ runtimeError("Division by zero.");
+ }
+
+ if (op1->isNULL() || op2->isNULL() || op2->getInt() == 0) {
+ _operand->setNULL();
+ } else {
+ _operand->setInt(op1->getInt() % op2->getInt());
+ }
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_NOT:
+ op1 = _stack->pop();
+ //if (op1->isNULL()) _operand->setNULL();
+ if (op1->isNULL()) {
+ _operand->setBool(true);
+ } else {
+ _operand->setBool(!op1->getBool());
+ }
+ _stack->push(_operand);
+
+ break;
+
+ case II_AND:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+ if (op1 == NULL || op2 == NULL) {
+ runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ _operand->setBool(false);
+ } else {
+ _operand->setBool(op1->getBool() && op2->getBool());
+ }
+ _stack->push(_operand);
+ break;
+
+ case II_OR:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+ if (op1 == NULL || op2 == NULL) {
+ runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ _operand->setBool(false);
+ } else {
+ _operand->setBool(op1->getBool() || op2->getBool());
+ }
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_EQ:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if ((op1->isNULL() && !op2->isNULL()) || (!op1->isNULL() && op2->isNULL())) _operand->setBool(false);
+ else if (op1->isNative() && op2->isNative()){
+ _operand->setBool(op1->getNative() == op2->getNative());
+ }
+ else if (op1->getType()==VAL_STRING || op2->getType()==VAL_STRING){
+ _operand->setBool(scumm_stricmp(op1->getString(), op2->getString())==0);
+ }
+ else if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() == op2->getFloat());
+ }
+ else{
+ _operand->setBool(op1->getInt() == op2->getInt());
+ }
+ */
+
+ _operand->setBool(ScValue::compare(op1, op2) == 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_NE:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if ((op1->isNULL() && !op2->isNULL()) || (!op1->isNULL() && op2->isNULL())) _operand->setBool(true);
+ else if (op1->isNative() && op2->isNative()){
+ _operand->setBool(op1->getNative() != op2->getNative());
+ }
+ else if (op1->getType()==VAL_STRING || op2->getType()==VAL_STRING){
+ _operand->setBool(scumm_stricmp(op1->getString(), op2->getString())!=0);
+ }
+ else if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() != op2->getFloat());
+ }
+ else{
+ _operand->setBool(op1->getInt() != op2->getInt());
+ }
+ */
+
+ _operand->setBool(ScValue::compare(op1, op2) != 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_L:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() < op2->getFloat());
+ }
+ else _operand->setBool(op1->getInt() < op2->getInt());
+ */
+
+ _operand->setBool(ScValue::compare(op1, op2) < 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_G:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() > op2->getFloat());
+ }
+ else _operand->setBool(op1->getInt() > op2->getInt());
+ */
+
+ _operand->setBool(ScValue::compare(op1, op2) > 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_LE:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() <= op2->getFloat());
+ }
+ else _operand->setBool(op1->getInt() <= op2->getInt());
+ */
+
+ _operand->setBool(ScValue::compare(op1, op2) <= 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_GE:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() >= op2->getFloat());
+ }
+ else _operand->setBool(op1->getInt() >= op2->getInt());
+ */
+
+ _operand->setBool(ScValue::compare(op1, op2) >= 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_STRICT_EQ:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ //_operand->setBool(op1->getType()==op2->getType() && op1->getFloat()==op2->getFloat());
+ _operand->setBool(ScValue::compareStrict(op1, op2) == 0);
+ _stack->push(_operand);
+
+ break;
+
+ case II_CMP_STRICT_NE:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ //_operand->setBool(op1->getType()!=op2->getType() || op1->getFloat()!=op2->getFloat());
+ _operand->setBool(ScValue::compareStrict(op1, op2) != 0);
+ _stack->push(_operand);
+ break;
+
+ case II_DBG_LINE: {
+ int newLine = getDWORD();
+ if (newLine != _currentLine) {
+ _currentLine = newLine;
+ }
+ break;
+
+ }
+ default:
+ _gameRef->LOG(0, "Fatal: Invalid instruction %d ('%s', line %d, IP:0x%x)\n", inst, _filename, _currentLine, _iP - sizeof(uint32));
+ _state = SCRIPT_FINISHED;
+ ret = STATUS_FAILED;
+ } // switch(instruction)
+
+ //delete op;
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 ScScript::getFuncPos(const Common::String &name) {
+ for (uint32 i = 0; i < _numFunctions; i++) {
+ if (name == _functions[i].name) {
+ return _functions[i].pos;
+ }
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 ScScript::getMethodPos(const Common::String &name) {
+ for (uint32 i = 0; i < _numMethods; i++) {
+ if (name == _methods[i].name) {
+ return _methods[i].pos;
+ }
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *ScScript::getVar(char *name) {
+ ScValue *ret = NULL;
+
+ // scope locals
+ if (_scopeStack->_sP >= 0) {
+ if (_scopeStack->getTop()->propExists(name)) {
+ ret = _scopeStack->getTop()->getProp(name);
+ }
+ }
+
+ // script globals
+ if (ret == NULL) {
+ if (_globals->propExists(name)) {
+ ret = _globals->getProp(name);
+ }
+ }
+
+ // engine globals
+ if (ret == NULL) {
+ if (_engine->_globals->propExists(name)) {
+ ret = _engine->_globals->getProp(name);
+ }
+ }
+
+ if (ret == NULL) {
+ //RuntimeError("Variable '%s' is inaccessible in the current block. Consider changing the script.", name);
+ _gameRef->LOG(0, "Warning: variable '%s' is inaccessible in the current block. Consider changing the script (script:%s, line:%d)", name, _filename, _currentLine);
+ ScValue *val = new ScValue(_gameRef);
+ ScValue *scope = _scopeStack->getTop();
+ if (scope) {
+ scope->setProp(name, val);
+ ret = _scopeStack->getTop()->getProp(name);
+ } else {
+ _globals->setProp(name, val);
+ ret = _globals->getProp(name);
+ }
+ delete val;
+ }
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::waitFor(BaseObject *object) {
+ if (_unbreakable) {
+ runtimeError("Script cannot be interrupted.");
+ return STATUS_OK;
+ }
+
+ _state = SCRIPT_WAITING;
+ _waitObject = object;
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::waitForExclusive(BaseObject *object) {
+ _engine->resetObject(object);
+ return waitFor(object);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::sleep(uint32 duration) {
+ if (_unbreakable) {
+ runtimeError("Script cannot be interrupted.");
+ return STATUS_OK;
+ }
+
+ _state = SCRIPT_SLEEPING;
+ if (_gameRef->_state == GAME_FROZEN) {
+ _waitTime = g_system->getMillis() + duration;
+ _waitFrozen = true;
+ } else {
+ _waitTime = _gameRef->_timer + duration;
+ _waitFrozen = false;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::finish(bool includingThreads) {
+ if (_state != SCRIPT_FINISHED && includingThreads) {
+ _state = SCRIPT_FINISHED;
+ finishThreads();
+ } else {
+ _state = SCRIPT_FINISHED;
+ }
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::run() {
+ _state = SCRIPT_RUNNING;
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void ScScript::runtimeError(const char *fmt, ...) {
+ char buff[256];
+ va_list va;
+
+ va_start(va, fmt);
+ vsprintf(buff, fmt, va);
+ va_end(va);
+
+ _gameRef->LOG(0, "Runtime error. Script '%s', line %d", _filename, _currentLine);
+ _gameRef->LOG(0, " %s", buff);
+
+ if (!_gameRef->_suppressScriptErrors) {
+ _gameRef->quickMessage("Script runtime error. View log for details.");
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::persist(BasePersistenceManager *persistMgr) {
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ // buffer
+ if (persistMgr->getIsSaving()) {
+ if (_state != SCRIPT_PERSISTENT && _state != SCRIPT_FINISHED && _state != SCRIPT_THREAD_FINISHED) {
+ persistMgr->transfer(TMEMBER(_bufferSize));
+ persistMgr->putBytes(_buffer, _bufferSize);
+ } else {
+ // don't save idle/finished scripts
+ int bufferSize = 0;
+ persistMgr->transfer(TMEMBER(bufferSize));
+ }
+ } else {
+ persistMgr->transfer(TMEMBER(_bufferSize));
+ if (_bufferSize > 0) {
+ _buffer = new byte[_bufferSize];
+ persistMgr->getBytes(_buffer, _bufferSize);
+ _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize);
+ initTables();
+ } else {
+ _buffer = NULL;
+ _scriptStream = NULL;
+ }
+ }
+
+ persistMgr->transfer(TMEMBER(_callStack));
+ persistMgr->transfer(TMEMBER(_currentLine));
+ persistMgr->transfer(TMEMBER(_engine));
+ persistMgr->transfer(TMEMBER(_filename));
+ persistMgr->transfer(TMEMBER(_freezable));
+ persistMgr->transfer(TMEMBER(_globals));
+ persistMgr->transfer(TMEMBER(_iP));
+ persistMgr->transfer(TMEMBER(_scopeStack));
+ persistMgr->transfer(TMEMBER(_stack));
+ persistMgr->transfer(TMEMBER_INT(_state));
+ persistMgr->transfer(TMEMBER(_operand));
+ persistMgr->transfer(TMEMBER_INT(_origState));
+ persistMgr->transfer(TMEMBER(_owner));
+ persistMgr->transfer(TMEMBER(_reg1));
+ persistMgr->transfer(TMEMBER(_thread));
+ persistMgr->transfer(TMEMBER(_threadEvent));
+ persistMgr->transfer(TMEMBER(_thisStack));
+ persistMgr->transfer(TMEMBER(_timeSlice));
+ persistMgr->transfer(TMEMBER(_waitObject));
+ persistMgr->transfer(TMEMBER(_waitScript));
+ persistMgr->transfer(TMEMBER(_waitTime));
+ persistMgr->transfer(TMEMBER(_waitFrozen));
+
+ persistMgr->transfer(TMEMBER(_methodThread));
+ persistMgr->transfer(TMEMBER(_methodThread));
+ persistMgr->transfer(TMEMBER(_unbreakable));
+ persistMgr->transfer(TMEMBER(_parentScript));
+
+ if (!persistMgr->getIsSaving()) {
+ _tracingMode = false;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScScript *ScScript::invokeEventHandler(const Common::String &eventName, bool unbreakable) {
+ //if (_state!=SCRIPT_PERSISTENT) return NULL;
+
+ uint32 pos = getEventPos(eventName);
+ if (!pos) {
+ return NULL;
+ }
+
+ ScScript *thread = new ScScript(_gameRef, _engine);
+ if (thread) {
+ bool ret = thread->createThread(this, pos, eventName);
+ if (DID_SUCCEED(ret)) {
+ thread->_unbreakable = unbreakable;
+ _engine->_scripts.add(thread);
+ return thread;
+ } else {
+ delete thread;
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 ScScript::getEventPos(const Common::String &name) {
+ for (int i = _numEvents - 1; i >= 0; i--) {
+ if (scumm_stricmp(name.c_str(), _events[i].name) == 0) {
+ return _events[i].pos;
+ }
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::canHandleEvent(const Common::String &eventName) {
+ return getEventPos(eventName) != 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::canHandleMethod(const Common::String &methodName) {
+ return getMethodPos(methodName) != 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::pause() {
+ if (_state == SCRIPT_PAUSED) {
+ _gameRef->LOG(0, "Attempting to pause a paused script ('%s', line %d)", _filename, _currentLine);
+ return STATUS_FAILED;
+ }
+
+ if (!_freezable || _state == SCRIPT_PERSISTENT) {
+ return STATUS_OK;
+ }
+
+ _origState = _state;
+ _state = SCRIPT_PAUSED;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::resume() {
+ if (_state != SCRIPT_PAUSED) {
+ return STATUS_OK;
+ }
+
+ _state = _origState;
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScScript::TExternalFunction *ScScript::getExternal(char *name) {
+ for (uint32 i = 0; i < _numExternals; i++) {
+ if (strcmp(name, _externals[i].name) == 0) {
+ return &_externals[i];
+ }
+ }
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::externalCall(ScStack *stack, ScStack *thisStack, ScScript::TExternalFunction *function) {
+
+ _gameRef->LOG(0, "External functions are not supported on this platform.");
+ stack->correctParams(0);
+ stack->pushNULL();
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::copyParameters(ScStack *stack) {
+ int i;
+ int numParams = stack->pop()->getInt();
+ for (i = numParams - 1; i >= 0; i--) {
+ _stack->push(stack->getAt(i));
+ }
+ _stack->pushInt(numParams);
+
+ for (i = 0; i < numParams; i++) {
+ stack->pop();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScScript::finishThreads() {
+ for (uint32 i = 0; i < _engine->_scripts.size(); i++) {
+ ScScript *scr = _engine->_scripts[i];
+ if (scr->_thread && scr->_state != SCRIPT_FINISHED && scr->_owner == _owner && scumm_stricmp(scr->_filename, _filename) == 0) {
+ scr->finish(true);
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// IWmeDebugScript interface implementation
+int ScScript::dbgGetLine() {
+ return _currentLine;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *ScScript::dbgGetFilename() {
+ return _filename;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void ScScript::afterLoad() {
+ if (_buffer == NULL) {
+ byte *buffer = _engine->getCompiledScript(_filename, &_bufferSize);
+ if (!buffer) {
+ _gameRef->LOG(0, "Error reinitializing script '%s' after load. Script will be terminated.", _filename);
+ _state = SCRIPT_ERROR;
+ return;
+ }
+
+ _buffer = new byte [_bufferSize];
+ memcpy(_buffer, buffer, _bufferSize);
+
+ delete _scriptStream;
+ _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize);
+
+ initTables();
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script.h b/engines/wintermute/base/scriptables/script.h
new file mode 100644
index 0000000000..0616bce58a
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script.h
@@ -0,0 +1,174 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SCSCRIPT_H
+#define WINTERMUTE_SCSCRIPT_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/base/scriptables/dcscript.h" // Added by ClassView
+#include "engines/wintermute/coll_templ.h"
+
+namespace Wintermute {
+class BaseScriptHolder;
+class BaseObject;
+class ScEngine;
+class ScStack;
+class ScScript : public BaseClass {
+public:
+ BaseArray<int> _breakpoints;
+ bool _tracingMode;
+
+ ScScript *_parentScript;
+ bool _unbreakable;
+ bool finishThreads();
+ bool copyParameters(ScStack *stack);
+
+ void afterLoad();
+private:
+ ScValue *_operand;
+ ScValue *_reg1;
+public:
+ bool _freezable;
+ bool resume();
+ bool pause();
+ bool canHandleEvent(const Common::String &eventName);
+ bool canHandleMethod(const Common::String &methodName);
+ bool createThread(ScScript *original, uint32 initIP, const Common::String &eventName);
+ bool createMethodThread(ScScript *original, const Common::String &methodName);
+ ScScript *invokeEventHandler(const Common::String &eventName, bool unbreakable = false);
+ uint32 _timeSlice;
+ DECLARE_PERSISTENT(ScScript, BaseClass)
+ void runtimeError(const char *fmt, ...);
+ bool run();
+ bool finish(bool includingThreads = false);
+ bool sleep(uint32 duration);
+ bool waitForExclusive(BaseObject *object);
+ bool waitFor(BaseObject *object);
+ uint32 _waitTime;
+ bool _waitFrozen;
+ BaseObject *_waitObject;
+ ScScript *_waitScript;
+ TScriptState _state;
+ TScriptState _origState;
+ ScValue *getVar(char *name);
+ uint32 getFuncPos(const Common::String &name);
+ uint32 getEventPos(const Common::String &name);
+ uint32 getMethodPos(const Common::String &name);
+ typedef struct {
+ uint32 magic;
+ uint32 version;
+ uint32 codeStart;
+ uint32 funcTable;
+ uint32 symbolTable;
+ uint32 eventTable;
+ uint32 externalsTable;
+ uint32 methodTable;
+ } TScriptHeader;
+
+ TScriptHeader _header;
+
+ typedef struct {
+ char *name;
+ uint32 pos;
+ } TFunctionPos;
+
+ typedef struct {
+ char *name;
+ uint32 pos;
+ } TMethodPos;
+
+ typedef struct {
+ char *name;
+ uint32 pos;
+ } TEventPos;
+
+ typedef struct {
+ char *name;
+ char *dll_name;
+ TCallType call_type;
+ TExternalType returns;
+ int nu_params;
+ TExternalType *params;
+ } TExternalFunction;
+
+
+ ScStack *_callStack;
+ ScStack *_thisStack;
+ ScStack *_scopeStack;
+ ScStack *_stack;
+ ScValue *_globals;
+ ScEngine *_engine;
+ int _currentLine;
+ bool executeInstruction();
+ char *getString();
+ uint32 getDWORD();
+ double getFloat();
+ void cleanup();
+ bool create(const char *filename, byte *buffer, uint32 size, BaseScriptHolder *owner);
+ uint32 _iP;
+private:
+ void readHeader();
+ uint32 _bufferSize;
+ byte *_buffer;
+public:
+ Common::SeekableReadStream *_scriptStream;
+ ScScript(BaseGame *inGame, ScEngine *engine);
+ virtual ~ScScript();
+ char *_filename;
+ bool _thread;
+ bool _methodThread;
+ char *_threadEvent;
+ BaseScriptHolder *_owner;
+ ScScript::TExternalFunction *getExternal(char *name);
+ bool externalCall(ScStack *stack, ScStack *thisStack, ScScript::TExternalFunction *function);
+private:
+ char **_symbols;
+ uint32 _numSymbols;
+ TFunctionPos *_functions;
+ TMethodPos *_methods;
+ TEventPos *_events;
+ uint32 _numExternals;
+ TExternalFunction *_externals;
+ uint32 _numFunctions;
+ uint32 _numMethods;
+ uint32 _numEvents;
+
+ bool initScript();
+ bool initTables();
+
+
+// IWmeDebugScript interface implementation
+public:
+ virtual int dbgGetLine();
+ virtual const char *dbgGetFilename();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_engine.cpp b/engines/wintermute/base/scriptables/script_engine.cpp
new file mode 100644
index 0000000000..3d1863946e
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_engine.cpp
@@ -0,0 +1,609 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/script_engine.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_ext_math.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/utils/utils.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(ScEngine, true)
+
+#define COMPILER_DLL "dcscomp.dll"
+//////////////////////////////////////////////////////////////////////////
+ScEngine::ScEngine(BaseGame *inGame) : BaseClass(inGame) {
+ _gameRef->LOG(0, "Initializing scripting engine...");
+
+ if (_compilerAvailable) {
+ _gameRef->LOG(0, " Script compiler bound successfuly");
+ } else {
+ _gameRef->LOG(0, " Script compiler is NOT available");
+ }
+
+ _globals = new ScValue(_gameRef);
+
+
+ // register 'Game' as global variable
+ if (!_globals->propExists("Game")) {
+ ScValue val(_gameRef);
+ val.setNative(_gameRef, true);
+ _globals->setProp("Game", &val);
+ }
+
+ // register 'Math' as global variable
+ if (!_globals->propExists("Math")) {
+ ScValue val(_gameRef);
+ val.setNative(_gameRef->_mathClass, true);
+ _globals->setProp("Math", &val);
+ }
+
+ // prepare script cache
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ _cachedScripts[i] = NULL;
+ }
+
+ _currentScript = NULL;
+
+ _isProfiling = false;
+ _profilingStartTime = 0;
+
+ //EnableProfiling();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScEngine::~ScEngine() {
+ _gameRef->LOG(0, "Shutting down scripting engine");
+
+ disableProfiling();
+
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::cleanup() {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (!_scripts[i]->_thread && _scripts[i]->_owner) {
+ _scripts[i]->_owner->removeScript(_scripts[i]);
+ }
+ delete _scripts[i];
+ _scripts.remove_at(i);
+ i--;
+ }
+
+ _scripts.clear();
+
+ delete _globals;
+ _globals = NULL;
+
+ emptyScriptCache();
+
+ _currentScript = NULL; // ref only
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte *ScEngine::loadFile(void *data, char *filename, uint32 *size) {
+ return BaseFileManager::getEngineInstance()->readWholeFile(filename, size);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScEngine::closeFile(void *data, byte *buffer) {
+ delete[] buffer;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScEngine::parseElement(void *data, int line, int type, void *elementData) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScScript *ScEngine::runScript(const char *filename, BaseScriptHolder *owner) {
+ byte *compBuffer;
+ uint32 compSize;
+
+ // get script from cache
+ compBuffer = getCompiledScript(filename, &compSize);
+ if (!compBuffer) {
+ return NULL;
+ }
+
+ // add new script
+ ScScript *script = new ScScript(_gameRef, this);
+ bool ret = script->create(filename, compBuffer, compSize, owner);
+ if (DID_FAIL(ret)) {
+ _gameRef->LOG(ret, "Error running script '%s'...", filename);
+ delete script;
+ return NULL;
+ } else {
+ // publish the "self" pseudo-variable
+ ScValue val(_gameRef);
+ if (owner) {
+ val.setNative(owner, true);
+ } else {
+ val.setNULL();
+ }
+
+ script->_globals->setProp("self", &val);
+ script->_globals->setProp("this", &val);
+
+ _scripts.add(script);
+
+ return script;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte *ScEngine::getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache) {
+ // is script in cache?
+ if (!ignoreCache) {
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ if (_cachedScripts[i] && scumm_stricmp(_cachedScripts[i]->_filename.c_str(), filename) == 0) {
+ _cachedScripts[i]->_timestamp = g_system->getMillis();
+ *outSize = _cachedScripts[i]->_size;
+ return _cachedScripts[i]->_buffer;
+ }
+ }
+ }
+
+ // nope, load it
+ byte *compBuffer;
+ uint32 compSize;
+
+ uint32 size;
+
+ byte *buffer = BaseEngine::instance().getFileManager()->readWholeFile(filename, &size);
+ if (!buffer) {
+ _gameRef->LOG(0, "ScEngine::GetCompiledScript - error opening script '%s'", filename);
+ return NULL;
+ }
+
+ // needs to be compiled?
+ if (FROM_LE_32(*(uint32 *)buffer) == SCRIPT_MAGIC) {
+ compBuffer = buffer;
+ compSize = size;
+ } else {
+ if (!_compilerAvailable) {
+ _gameRef->LOG(0, "ScEngine::GetCompiledScript - script '%s' needs to be compiled but compiler is not available", filename);
+ delete[] buffer;
+ return NULL;
+ }
+ // This code will never be called, since _compilerAvailable is const false.
+ // It's only here in the event someone would want to reinclude the compiler.
+ error("Script needs compilation, ScummVM does not contain a WME compiler");
+ }
+
+ byte *ret = NULL;
+
+ // add script to cache
+ CScCachedScript *cachedScript = new CScCachedScript(filename, compBuffer, compSize);
+ if (cachedScript) {
+ int index = 0;
+ uint32 minTime = g_system->getMillis();
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ if (_cachedScripts[i] == NULL) {
+ index = i;
+ break;
+ } else if (_cachedScripts[i]->_timestamp <= minTime) {
+ minTime = _cachedScripts[i]->_timestamp;
+ index = i;
+ }
+ }
+
+ if (_cachedScripts[index] != NULL) {
+ delete _cachedScripts[index];
+ }
+ _cachedScripts[index] = cachedScript;
+
+ ret = cachedScript->_buffer;
+ *outSize = cachedScript->_size;
+ }
+
+
+ // cleanup
+ delete[] buffer;
+
+ return ret;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::tick() {
+ if (_scripts.size() == 0) {
+ return STATUS_OK;
+ }
+
+
+ // resolve waiting scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+
+ switch (_scripts[i]->_state) {
+ case SCRIPT_WAITING: {
+ /*
+ bool obj_found=false;
+ for(int j=0; j<_gameRef->_regObjects.size(); j++)
+ {
+ if (_gameRef->_regObjects[j] == _scripts[i]->_waitObject)
+ {
+ if (_gameRef->_regObjects[j]->IsReady()) _scripts[i]->Run();
+ obj_found = true;
+ break;
+ }
+ }
+ if (!obj_found) _scripts[i]->finish(); // _waitObject no longer exists
+ */
+ if (_gameRef->validObject(_scripts[i]->_waitObject)) {
+ if (_scripts[i]->_waitObject->isReady()) {
+ _scripts[i]->run();
+ }
+ } else {
+ _scripts[i]->finish();
+ }
+ break;
+ }
+
+ case SCRIPT_SLEEPING: {
+ if (_scripts[i]->_waitFrozen) {
+ if (_scripts[i]->_waitTime <= g_system->getMillis()) {
+ _scripts[i]->run();
+ }
+ } else {
+ if (_scripts[i]->_waitTime <= _gameRef->_timer) {
+ _scripts[i]->run();
+ }
+ }
+ break;
+ }
+
+ case SCRIPT_WAITING_SCRIPT: {
+ if (!isValidScript(_scripts[i]->_waitScript) || _scripts[i]->_waitScript->_state == SCRIPT_ERROR) {
+ // fake return value
+ _scripts[i]->_stack->pushNULL();
+ _scripts[i]->_waitScript = NULL;
+ _scripts[i]->run();
+ } else {
+ if (_scripts[i]->_waitScript->_state == SCRIPT_THREAD_FINISHED) {
+ // copy return value
+ _scripts[i]->_stack->push(_scripts[i]->_waitScript->_stack->pop());
+ _scripts[i]->run();
+ _scripts[i]->_waitScript->finish();
+ _scripts[i]->_waitScript = NULL;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ } // switch
+ } // for each script
+
+
+ // execute scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+
+ // skip paused scripts
+ if (_scripts[i]->_state == SCRIPT_PAUSED) {
+ continue;
+ }
+
+ // time sliced script
+ if (_scripts[i]->_timeSlice > 0) {
+ uint32 startTime = g_system->getMillis();
+ while (_scripts[i]->_state == SCRIPT_RUNNING && g_system->getMillis() - startTime < _scripts[i]->_timeSlice) {
+ _currentScript = _scripts[i];
+ _scripts[i]->executeInstruction();
+ }
+ if (_isProfiling && _scripts[i]->_filename) {
+ addScriptTime(_scripts[i]->_filename, g_system->getMillis() - startTime);
+ }
+ }
+
+ // normal script
+ else {
+ uint32 startTime = 0;
+ bool isProfiling = _isProfiling;
+ if (isProfiling) {
+ startTime = g_system->getMillis();
+ }
+
+ while (_scripts[i]->_state == SCRIPT_RUNNING) {
+ _currentScript = _scripts[i];
+ _scripts[i]->executeInstruction();
+ }
+ if (isProfiling && _scripts[i]->_filename) {
+ addScriptTime(_scripts[i]->_filename, g_system->getMillis() - startTime);
+ }
+ }
+ _currentScript = NULL;
+ }
+
+ removeFinishedScripts();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::tickUnbreakable() {
+ // execute unbreakable scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (!_scripts[i]->_unbreakable) {
+ continue;
+ }
+
+ while (_scripts[i]->_state == SCRIPT_RUNNING) {
+ _currentScript = _scripts[i];
+ _scripts[i]->executeInstruction();
+ }
+ _scripts[i]->finish();
+ _currentScript = NULL;
+ }
+ removeFinishedScripts();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::removeFinishedScripts() {
+ // remove finished scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (_scripts[i]->_state == SCRIPT_FINISHED || _scripts[i]->_state == SCRIPT_ERROR) {
+ if (!_scripts[i]->_thread && _scripts[i]->_owner) {
+ _scripts[i]->_owner->removeScript(_scripts[i]);
+ }
+
+ delete _scripts[i];
+ _scripts.remove_at(i);
+ i--;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int ScEngine::getNumScripts(int *running, int *waiting, int *persistent) {
+ int numRunning = 0, numWaiting = 0, numPersistent = 0, numTotal = 0;
+
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (_scripts[i]->_state == SCRIPT_FINISHED) {
+ continue;
+ }
+ switch (_scripts[i]->_state) {
+ case SCRIPT_RUNNING:
+ case SCRIPT_SLEEPING:
+ case SCRIPT_PAUSED:
+ numRunning++;
+ break;
+ case SCRIPT_WAITING:
+ numWaiting++;
+ break;
+ case SCRIPT_PERSISTENT:
+ numPersistent++;
+ break;
+ default:
+ warning("ScEngine::GetNumScripts - unhandled enum");
+ break;
+ }
+ numTotal++;
+ }
+ if (running) {
+ *running = numRunning;
+ }
+ if (waiting) {
+ *waiting = numWaiting;
+ }
+ if (persistent) {
+ *persistent = numPersistent;
+ }
+
+ return numTotal;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::emptyScriptCache() {
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ if (_cachedScripts[i]) {
+ delete _cachedScripts[i];
+ _cachedScripts[i] = NULL;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::resetObject(BaseObject *Object) {
+ // terminate all scripts waiting for this object
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (_scripts[i]->_state == SCRIPT_WAITING && _scripts[i]->_waitObject == Object) {
+ if (!_gameRef->_compatKillMethodThreads) {
+ resetScript(_scripts[i]);
+ }
+
+ bool isThread = _scripts[i]->_methodThread || _scripts[i]->_thread;
+ _scripts[i]->finish(!isThread); // 1.9b1 - top-level script kills its threads as well
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::resetScript(ScScript *script) {
+ // terminate all scripts waiting for this script
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (_scripts[i]->_state == SCRIPT_WAITING_SCRIPT && _scripts[i]->_waitScript == script) {
+ _scripts[i]->finish();
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::persist(BasePersistenceManager *persistMgr) {
+ if (!persistMgr->getIsSaving()) {
+ cleanup();
+ }
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+ persistMgr->transfer(TMEMBER(_currentScript));
+ persistMgr->transfer(TMEMBER(_globals));
+ _scripts.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScEngine::editorCleanup() {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (_scripts[i]->_owner == NULL && (_scripts[i]->_state == SCRIPT_FINISHED || _scripts[i]->_state == SCRIPT_ERROR)) {
+ delete _scripts[i];
+ _scripts.remove_at(i);
+ i--;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::pauseAll() {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (_scripts[i] != _currentScript) {
+ _scripts[i]->pause();
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::resumeAll() {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ _scripts[i]->resume();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::isValidScript(ScScript *script) {
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ if (_scripts[i] == script) {
+ return true;
+ }
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ScEngine::clearGlobals(bool includingNatives) {
+ _globals->CleanProps(includingNatives);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void ScEngine::addScriptTime(const char *filename, uint32 time) {
+ if (!_isProfiling) {
+ return;
+ }
+
+ AnsiString fileName = filename;
+ fileName.toLowercase();
+ _scriptTimes[fileName] += time;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScEngine::enableProfiling() {
+ if (_isProfiling) {
+ return;
+ }
+
+ // destroy old data, if any
+ _scriptTimes.clear();
+
+ _profilingStartTime = g_system->getMillis();
+ _isProfiling = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScEngine::disableProfiling() {
+ if (!_isProfiling) {
+ return;
+ }
+
+ dumpStats();
+ _isProfiling = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScEngine::dumpStats() {
+ error("DumpStats not ported to ScummVM yet");
+ /* uint32 totalTime = g_system->getMillis() - _profilingStartTime;
+
+ typedef std::vector <std::pair<uint32, std::string> > TimeVector;
+ TimeVector times;
+
+ ScriptTimes::iterator it;
+ for (it = _scriptTimes.begin(); it != _scriptTimes.end(); ++it) {
+ times.push_back(std::pair<uint32, std::string> (it->_value, it->_key));
+ }
+ std::sort(times.begin(), times.end());
+
+
+ TimeVector::reverse_iterator tit;
+
+ _gameRef->LOG(0, "***** Script profiling information: *****");
+ _gameRef->LOG(0, " %-40s %fs", "Total execution time", (float)totalTime / 1000);
+
+ for (tit = times.rbegin(); tit != times.rend(); ++tit) {
+ _gameRef->LOG(0, " %-40s %fs (%f%%)", tit->second.c_str(), (float)tit->first / 1000, (float)tit->first / (float)totalTime * 100);
+ }*/
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_engine.h b/engines/wintermute/base/scriptables/script_engine.h
new file mode 100644
index 0000000000..1a023326eb
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_engine.h
@@ -0,0 +1,135 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SCENGINE_H
+#define WINTERMUTE_SCENGINE_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/base/base.h"
+
+namespace Wintermute {
+
+#define MAX_CACHED_SCRIPTS 20
+class ScScript;
+class ScValue;
+class BaseObject;
+class BaseScriptHolder;
+class ScEngine : public BaseClass {
+public:
+ class CScCachedScript {
+ public:
+ CScCachedScript(const char *filename, byte *buffer, uint32 size) {
+ _timestamp = g_system->getMillis();
+ _buffer = new byte[size];
+ if (_buffer) {
+ memcpy(_buffer, buffer, size);
+ }
+ _size = size;
+ _filename = filename;
+ };
+
+ ~CScCachedScript() {
+ if (_buffer) {
+ delete[] _buffer;
+ }
+ };
+
+ uint32 _timestamp;
+ byte *_buffer;
+ uint32 _size;
+ Common::String _filename;
+ };
+
+ class CScBreakpoint {
+ public:
+ CScBreakpoint(const char *filename) {
+ _filename = filename;
+ }
+
+ ~CScBreakpoint() {
+ _lines.clear();
+ }
+
+ Common::String _filename;
+ BaseArray<int> _lines;
+ };
+
+public:
+ bool clearGlobals(bool includingNatives = false);
+ bool tickUnbreakable();
+ bool removeFinishedScripts();
+ bool isValidScript(ScScript *script);
+
+ ScScript *_currentScript;
+ bool resumeAll();
+ bool pauseAll();
+ void editorCleanup();
+ bool resetObject(BaseObject *Object);
+ bool resetScript(ScScript *script);
+ bool emptyScriptCache();
+ byte *getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache = false);
+ DECLARE_PERSISTENT(ScEngine, BaseClass)
+ bool cleanup();
+ int getNumScripts(int *running = NULL, int *waiting = NULL, int *persistent = NULL);
+ bool tick();
+ ScValue *_globals;
+ ScScript *runScript(const char *filename, BaseScriptHolder *owner = NULL);
+ static const bool _compilerAvailable = false;
+
+ ScEngine(BaseGame *inGame);
+ virtual ~ScEngine();
+ static byte *loadFile(void *data, char *filename, uint32 *size);
+ static void closeFile(void *data, byte *buffer);
+ static void parseElement(void *data, int line, int type, void *elementData);
+
+ BaseArray<ScScript *> _scripts;
+
+ void enableProfiling();
+ void disableProfiling();
+ bool getIsProfiling() {
+ return _isProfiling;
+ }
+
+ void addScriptTime(const char *filename, uint32 Time);
+ void dumpStats();
+
+private:
+
+ CScCachedScript *_cachedScripts[MAX_CACHED_SCRIPTS];
+ bool _isProfiling;
+ uint32 _profilingStartTime;
+
+ typedef Common::HashMap<Common::String, uint32> ScriptTimes;
+ ScriptTimes _scriptTimes;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_ext_array.cpp b/engines/wintermute/base/scriptables/script_ext_array.cpp
new file mode 100644
index 0000000000..613cbd0758
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_array.cpp
@@ -0,0 +1,252 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/system/sys_instance.h"
+#include "engines/wintermute/base/scriptables/script_ext_array.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(SXArray, false)
+
+BaseScriptable *makeSXArray(BaseGame *inGame, ScStack *stack) {
+ return new SXArray(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXArray::SXArray(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
+ _length = 0;
+ _values = new ScValue(_gameRef);
+
+ int numParams = stack->pop()->getInt(0);
+
+ if (numParams == 1) {
+ _length = stack->pop()->getInt(0);
+ } else if (numParams > 1) {
+ _length = numParams;
+ char paramName[20];
+ for (int i = 0; i < numParams; i++) {
+ sprintf(paramName, "%d", i);
+ _values->setProp(paramName, stack->pop());
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXArray::SXArray(BaseGame *inGame) : BaseScriptable(inGame) {
+ _length = 0;
+ _values = new ScValue(_gameRef);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SXArray::~SXArray() {
+ delete _values;
+ _values = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *SXArray::scToString() {
+ char dummy[32768];
+ strcpy(dummy, "");
+ char propName[20];
+ for (int i = 0; i < _length; i++) {
+ sprintf(propName, "%d", i);
+ ScValue *val = _values->getProp(propName);
+ if (val) {
+ if (strlen(dummy) + strlen(val->getString()) < 32768) {
+ strcat(dummy, val->getString());
+ }
+ }
+
+ if (i < _length - 1 && strlen(dummy) + 1 < 32768) {
+ strcat(dummy, ",");
+ }
+ }
+ _strRep = dummy;
+ return _strRep.c_str();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXArray::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // Push
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Push") == 0) {
+ int numParams = stack->pop()->getInt(0);
+ char paramName[20];
+
+ for (int i = 0; i < numParams; i++) {
+ _length++;
+ sprintf(paramName, "%d", _length - 1);
+ _values->setProp(paramName, stack->pop(), true);
+ }
+ stack->pushInt(_length);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Pop
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Pop") == 0) {
+
+ stack->correctParams(0);
+
+ if (_length > 0) {
+ char paramName[20];
+ sprintf(paramName, "%d", _length - 1);
+ stack->push(_values->getProp(paramName));
+ _values->deleteProp(paramName);
+ _length--;
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *SXArray::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("array");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Length") {
+ _scValue->setInt(_length);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // [number]
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ char paramName[20];
+ if (validNumber(name.c_str(), paramName)) { // TODO: Change to Common::String
+ return _values->getProp(paramName);
+ } else {
+ return _scValue;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXArray::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Length") == 0) {
+ int origLength = _length;
+ _length = MAX(value->getInt(0), 0);
+
+ char propName[20];
+ if (_length < origLength) {
+ for (int i = _length; i < origLength; i++) {
+ sprintf(propName, "%d", i);
+ _values->deleteProp(propName);
+ }
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // [number]
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ char paramName[20];
+ if (validNumber(name, paramName)) {
+ int index = atoi(paramName);
+ if (index >= _length) {
+ _length = index + 1;
+ }
+ return _values->setProp(paramName, value);
+ } else {
+ return STATUS_FAILED;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXArray::persist(BasePersistenceManager *persistMgr) {
+ BaseScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_length));
+ persistMgr->transfer(TMEMBER(_values));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXArray::validNumber(const char *origStr, char *outStr) {
+ bool isNumber = true;
+ for (uint32 i = 0; i < strlen(origStr); i++) {
+ if (!(origStr[i] >= '0' && origStr[i] <= '9')) {
+ isNumber = false;
+ break;
+ }
+ }
+
+ if (isNumber) {
+ int index = atoi(origStr);
+ sprintf(outStr, "%d", index);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SXArray::push(ScValue *val) {
+ char paramName[20];
+ _length++;
+ sprintf(paramName, "%d", _length - 1);
+ _values->setProp(paramName, val, true);
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_ext_array.h b/engines/wintermute/base/scriptables/script_ext_array.h
new file mode 100644
index 0000000000..284c547a27
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_array.h
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXARRAY_H
+#define WINTERMUTE_SXARRAY_H
+
+#include "engines/wintermute/base/base_scriptable.h"
+
+namespace Wintermute {
+
+class SXArray : public BaseScriptable {
+public:
+ bool push(ScValue *val);
+ bool validNumber(const char *origStr, char *outStr);
+ DECLARE_PERSISTENT(SXArray, BaseScriptable)
+ SXArray(BaseGame *inGame, ScStack *stack);
+ SXArray(BaseGame *inGame);
+ virtual ~SXArray();
+ ScValue *scGetProperty(const Common::String &name);
+ bool scSetProperty(const char *name, ScValue *value);
+ bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ const char *scToString();
+private:
+ int _length;
+ ScValue *_values;
+ Common::String _strRep;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_ext_date.cpp b/engines/wintermute/base/scriptables/script_ext_date.cpp
new file mode 100644
index 0000000000..5aa069d0b2
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_date.cpp
@@ -0,0 +1,293 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_ext_date.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(SXDate, false)
+
+BaseScriptable *makeSXDate(BaseGame *inGame, ScStack *stack) {
+ return new SXDate(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXDate::SXDate(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
+ stack->correctParams(6);
+
+ memset(&_tm, 0, sizeof(_tm));
+
+ ScValue *valYear = stack->pop();
+ _tm.tm_year = valYear->getInt() - 1900;
+ _tm.tm_mon = stack->pop()->getInt() - 1;
+ _tm.tm_mday = stack->pop()->getInt();
+ _tm.tm_hour = stack->pop()->getInt();
+ _tm.tm_min = stack->pop()->getInt();
+ _tm.tm_sec = stack->pop()->getInt();
+
+ if (valYear->isNULL()) {
+ g_system->getTimeAndDate(_tm);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SXDate::~SXDate() {
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *SXDate::scToString() {
+ // TODO: Make this more stringy, and less ISO 8601-like
+ _strRep.format("%04d-%02d-%02d - %02d:%02d:%02d", _tm.tm_year, _tm.tm_mon, _tm.tm_mday, _tm.tm_hour, _tm.tm_min, _tm.tm_sec);
+ return _strRep.c_str();
+ //return asctime(&_tm);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXDate::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // GetYear
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetYear") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_year + 1900);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetMonth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetMonth") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_mon + 1);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetDate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetDate") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_mday);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetHours
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHours") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_hour);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetMinutes
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetMinutes") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_min);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetSeconds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSeconds") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_sec);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetWeekday
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetWeekday") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_wday);
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetYear
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetYear") == 0) {
+ stack->correctParams(1);
+ _tm.tm_year = stack->pop()->getInt() - 1900;
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetMonth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetMonth") == 0) {
+ stack->correctParams(1);
+ _tm.tm_mon = stack->pop()->getInt() - 1;
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetDate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetDate") == 0) {
+ stack->correctParams(1);
+ _tm.tm_mday = stack->pop()->getInt();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetHours
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetHours") == 0) {
+ stack->correctParams(1);
+ _tm.tm_hour = stack->pop()->getInt();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetMinutes
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetMinutes") == 0) {
+ stack->correctParams(1);
+ _tm.tm_min = stack->pop()->getInt();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetSeconds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetSeconds") == 0) {
+ stack->correctParams(1);
+ _tm.tm_sec = stack->pop()->getInt();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetCurrentTime
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetCurrentTime") == 0) {
+ stack->correctParams(0);
+ g_system->getTimeAndDate(_tm);
+ stack->pushNULL();
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *SXDate::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("date");
+ return _scValue;
+ } else {
+ return _scValue;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXDate::scSetProperty(const char *name, ScValue *value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Name")==0){
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ else*/ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXDate::persist(BasePersistenceManager *persistMgr) {
+
+ BaseScriptable::persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_tm.tm_year));
+ persistMgr->transfer(TMEMBER(_tm.tm_mon));
+ persistMgr->transfer(TMEMBER(_tm.tm_mday));
+ persistMgr->transfer(TMEMBER(_tm.tm_hour));
+ persistMgr->transfer(TMEMBER(_tm.tm_min));
+ persistMgr->transfer(TMEMBER(_tm.tm_sec));
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int SXDate::scCompare(BaseScriptable *Value) {
+ TimeDate time1 = _tm;
+ TimeDate time2 = ((SXDate *)Value)->_tm;
+
+ if (time1.tm_year < time2.tm_year) {
+ return -1;
+ } else if (time1.tm_year == time2.tm_year) {
+ if (time1.tm_mon < time2.tm_mon) {
+ return -1;
+ } else if (time1.tm_mon == time2.tm_mon) {
+ if (time1.tm_mday < time2.tm_mday) {
+ return -1;
+ } else if (time1.tm_mday == time2.tm_mday) {
+ if (time1.tm_hour < time2.tm_hour) {
+ return -1;
+ } else if (time1.tm_hour == time2.tm_hour) {
+ if (time1.tm_min < time2.tm_min) {
+ return -1;
+ } else if (time1.tm_min == time2.tm_min) {
+ if (time1.tm_sec < time2.tm_sec) {
+ return -1;
+ } else if (time1.tm_sec == time2.tm_sec) {
+ return 0; // Equal
+ } else {
+ return 1; // Sec
+ }
+ } else {
+ return 1; // Minute
+ }
+ } else {
+ return 1; // Hour
+ }
+ } else {
+ return 1; // Day
+ }
+ } else {
+ return 1; // Month
+ }
+ } else {
+ return 1; // Year
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_ext_date.h b/engines/wintermute/base/scriptables/script_ext_date.h
new file mode 100644
index 0000000000..062b7c55c7
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_date.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXDATE_H
+#define WINTERMUTE_SXDATE_H
+
+#include "common/system.h"
+#include "engines/wintermute/base/base_scriptable.h"
+
+namespace Wintermute {
+
+class SXDate : public BaseScriptable {
+public:
+ int scCompare(BaseScriptable *Value);
+ DECLARE_PERSISTENT(SXDate, BaseScriptable)
+ SXDate(BaseGame *inGame, ScStack *Stack);
+ virtual ~SXDate();
+ ScValue *scGetProperty(const Common::String &name);
+ bool scSetProperty(const char *name, ScValue *value);
+ bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ const char *scToString();
+private:
+ TimeDate _tm;
+ Common::String _strRep;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_ext_file.cpp b/engines/wintermute/base/scriptables/script_ext_file.cpp
new file mode 100644
index 0000000000..a1d39c5d0a
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_file.cpp
@@ -0,0 +1,828 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/system/sys_class_registry.h"
+#include "engines/wintermute/system/sys_class.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/scriptables/script_ext_file.h"
+
+// Note: This code is completely untested, as I have yet to find a game that uses SXFile.
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(SXFile, false)
+
+BaseScriptable *makeSXFile(BaseGame *inGame, ScStack *stack) {
+ return new SXFile(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXFile::SXFile(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ _filename = NULL;
+ if (!val->isNULL()) {
+ BaseUtils::setString(&_filename, val->getString());
+ }
+
+ _readFile = NULL;
+ _writeFile = NULL;
+
+ _mode = 0;
+ _textMode = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SXFile::~SXFile() {
+ cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SXFile::cleanup() {
+ delete[] _filename;
+ _filename = NULL;
+ close();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void SXFile::close() {
+ if (_readFile) {
+ BaseFileManager::getEngineInstance()->closeFile(_readFile);
+ _readFile = NULL;
+ }
+ if (_writeFile) {
+ _writeFile->finalize();
+ delete _writeFile;
+ _writeFile = NULL;
+ }
+ _mode = 0;
+ _textMode = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *SXFile::scToString() {
+ if (_filename) {
+ return _filename;
+ } else {
+ return "[file object]";
+ }
+}
+
+#define FILE_BUFFER_SIZE 32768
+//////////////////////////////////////////////////////////////////////////
+bool SXFile::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetFilename
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetFilename") == 0) {
+ stack->correctParams(1);
+ const char *filename = stack->pop()->getString();
+ cleanup();
+ BaseUtils::setString(&_filename, filename);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // OpenAsText / OpenAsBinary
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "OpenAsText") == 0 || strcmp(name, "OpenAsBinary") == 0) {
+ stack->correctParams(1);
+ close();
+ _mode = stack->pop()->getInt(1);
+ if (_mode < 1 || _mode > 3) {
+ script->runtimeError("File.%s: invalid access mode. Setting read mode.", name);
+ _mode = 1;
+ }
+ if (_mode == 1) {
+ _readFile = BaseFileManager::getEngineInstance()->openFile(_filename);
+ if (!_readFile) {
+ //script->runtimeError("File.%s: Error opening file '%s' for reading.", Name, _filename);
+ close();
+ } else {
+ _textMode = strcmp(name, "OpenAsText") == 0;
+ }
+ } else {
+ if (strcmp(name, "OpenAsText") == 0) {
+ if (_mode == 2) {
+ _writeFile = openForWrite(_filename, false);
+ } else {
+ _writeFile = openForAppend(_filename, false);
+ }
+ } else {
+ if (_mode == 2) {
+ _writeFile = openForWrite(_filename, true);
+ } else {
+ _writeFile = openForAppend(_filename, true);
+ }
+ }
+
+ if (!_writeFile) {
+ //script->runtimeError("File.%s: Error opening file '%s' for writing.", Name, _filename);
+ close();
+ } else {
+ _textMode = strcmp(name, "OpenAsText") == 0;
+ }
+ }
+
+ if (_readFile || _writeFile) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Close
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Close") == 0) {
+ stack->correctParams(0);
+ close();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetPosition
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetPosition") == 0) {
+ stack->correctParams(1);
+ if (_mode == 0) {
+ script->runtimeError("File.%s: File is not open", name);
+ stack->pushBool(false);
+ } else {
+ int pos = stack->pop()->getInt();
+ stack->pushBool(setPos(pos));
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Delete
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Delete") == 0) {
+ stack->correctParams(0);
+ close();
+ error("SXFile-Method: \"Delete\" not supported");
+ //stack->pushBool(BasePlatform::deleteFile(_filename) != false);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Copy
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Copy") == 0) {
+ stack->correctParams(2);
+ /* const char *dest = */ stack->pop()->getString();
+ /* bool overwrite = */ stack->pop()->getBool(true);
+
+ close();
+ error("SXFile-Method: Copy not supported");
+ //stack->pushBool(BasePlatform::copyFile(_filename, Dest, !Overwrite) != false);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadLine
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadLine") == 0) {
+ stack->correctParams(0);
+ if (!_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open in text mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ uint32 bufSize = FILE_BUFFER_SIZE;
+ byte *buf = (byte *)malloc(bufSize);
+ uint32 counter = 0;
+ byte b;
+ bool foundNewLine = false;
+ bool ret = STATUS_FAILED;
+ do {
+ ret = _readFile->read(&b, 1);
+ if (ret != 1) {
+ break;
+ }
+
+ if (counter > bufSize) {
+ buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE);
+ bufSize += FILE_BUFFER_SIZE;
+ }
+ if (b == '\n') {
+ buf[counter] = '\0';
+ foundNewLine = true;
+ break;
+ } else if (b == 0x0D) {
+ continue;
+ } else {
+ buf[counter] = b;
+ counter++;
+ }
+ } while (DID_SUCCEED(ret));
+
+ if (counter > bufSize) {
+ buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE);
+ bufSize += FILE_BUFFER_SIZE;
+ }
+ buf[counter] = '\0';
+
+ if (!foundNewLine && counter == 0) {
+ stack->pushNULL();
+ } else {
+ stack->pushString((char *)buf);
+ }
+
+ free(buf);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadText
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadText") == 0) {
+ stack->correctParams(1);
+ int textLen = stack->pop()->getInt();
+
+ if (!_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open in text mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ uint32 bufSize = FILE_BUFFER_SIZE;
+ byte *buf = (byte *)malloc(bufSize);
+ uint32 counter = 0;
+ byte b;
+
+ bool ret = STATUS_FAILED;
+ while (counter < (uint32)textLen) {
+ ret = _readFile->read(&b, 1);
+ if (ret != 1) {
+ break;
+ }
+
+ if (counter > bufSize) {
+ buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE);
+ bufSize += FILE_BUFFER_SIZE;
+ }
+ if (b == 0x0D) {
+ continue;
+ } else {
+ buf[counter] = b;
+ counter++;
+ }
+ }
+
+ if (counter > bufSize) {
+ buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE);
+ bufSize += FILE_BUFFER_SIZE;
+ }
+ buf[counter] = '\0';
+
+ if (textLen > 0 && counter == 0) {
+ stack->pushNULL();
+ } else {
+ stack->pushString((char *)buf);
+ }
+
+ free(buf);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteLine / WriteText
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteLine") == 0 || strcmp(name, "WriteText") == 0) {
+ stack->correctParams(1);
+ const char *line = stack->pop()->getString();
+ if (!_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in text mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ Common::String writeLine;
+ if (strcmp(name, "WriteLine") == 0) {
+ writeLine = Common::String::format("%s\n", line);
+ } else {
+ writeLine = Common::String::format("%s", line);
+ }
+ _writeFile->writeString(writeLine);
+ _writeFile->writeByte(0);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+ // ReadBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadBool") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ bool val;
+ if (_readFile->read(&val, sizeof(bool)) == sizeof(bool)) {
+ stack->pushBool(val);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadByte
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadByte") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ byte val = _readFile->readByte();
+ if (!_readFile->err()) {
+ stack->pushInt(val);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadShort") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ int16 val = _readFile->readSint16LE();
+ if (!_readFile->err()) {
+ stack->pushInt(65536 + val);
+ } else {
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadInt / ReadLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadInt") == 0 || strcmp(name, "ReadLong") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ int32 val = _readFile->readSint32LE();
+ if (!_readFile->err()) {
+ stack->pushInt(val);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadFloat") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ float val;
+ (*(uint32 *)&val) = _readFile->readUint32LE();
+ if (!_readFile->err()) {
+ stack->pushFloat(val);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadDouble") == 0) { // TODO: Solve reading a 8 byte double.
+ error("SXFile::ReadDouble - Not endian safe yet");
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ double val;
+ if (_readFile->read(&val, sizeof(double)) == sizeof(double)) {
+ stack->pushFloat(val);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadString") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ uint32 size = _readFile->readUint32LE();
+ if (!_readFile->err()) {
+ byte *str = new byte[size + 1];
+ if (str) {
+ if (_readFile->read(str, size) == size) {
+ str[size] = '\0';
+ stack->pushString((char *)str);
+ }
+ delete[] str;
+ } else {
+ stack->pushNULL();
+ }
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteBool") == 0) {
+ stack->correctParams(1);
+ bool val = stack->pop()->getBool();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ _writeFile->writeByte(val);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteByte
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteByte") == 0) {
+ stack->correctParams(1);
+ byte val = stack->pop()->getInt();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ _writeFile->writeByte(val);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteShort") == 0) {
+ stack->correctParams(1);
+ int16 val = stack->pop()->getInt();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ _writeFile->writeSint16LE(val);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteInt / WriteLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteInt") == 0 || strcmp(name, "WriteLong") == 0) {
+ stack->correctParams(1);
+ int32 val = stack->pop()->getInt();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ _writeFile->writeSint32LE(val);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteFloat") == 0) {
+ stack->correctParams(1);
+ float val = stack->pop()->getFloat();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ uint32 *ptr = (uint32 *)&val;
+ _writeFile->writeUint32LE(*ptr);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteDouble") == 0) {
+ error("SXFile::WriteDouble - Not endian safe yet");
+ stack->correctParams(1);
+ /* double val = */ stack->pop()->getFloat();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ //fwrite(&val, sizeof(val), 1, (FILE *)_writeFile);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteString") == 0) {
+ stack->correctParams(1);
+ const char *val = stack->pop()->getString();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ uint32 size = strlen(val);
+ _writeFile->writeUint32LE(size);
+ _writeFile->writeString(val);
+
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ } else {
+ return BaseScriptable::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *SXFile::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("file");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Filename (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Filename") {
+ _scValue->setString(_filename);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Position (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Position") {
+ _scValue->setInt(getPos());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Length (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Length") {
+ _scValue->setInt(getLength());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TextMode (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TextMode") {
+ _scValue->setBool(_textMode);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccessMode (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "AccessMode") {
+ _scValue->setInt(_mode);
+ return _scValue;
+ } else {
+ return BaseScriptable::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXFile::scSetProperty(const char *name, ScValue *value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Length")==0){
+ int origLength = _length;
+ _length = max(value->getInt(0), 0);
+
+ char propName[20];
+ if (_length < OrigLength){
+ for(int i=_length; i<OrigLength; i++){
+ sprintf(PropName, "%d", i);
+ _values->DeleteProp(PropName);
+ }
+ }
+ return STATUS_OK;
+ }
+ else*/ return BaseScriptable::scSetProperty(name, value);
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 SXFile::getPos() {
+ if (_mode == 1 && _readFile) {
+ return _readFile->pos();
+ } else if ((_mode == 2 || _mode == 3) && _writeFile) {
+ error("SXFile - getPos for WriteFile not supported");
+ return 0;
+// return ftell((FILE *)_writeFile);
+ } else {
+ return 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SXFile::setPos(uint32 pos, int whence) {
+ if (_mode == 1 && _readFile) {
+ return _readFile->seek(pos, whence);
+ } else if ((_mode == 2 || _mode == 3) && _writeFile) {
+ error("SXFile - seeking in WriteFile not supported");
+ return false;
+// return fseek((FILE *)_writeFile, pos, (int)origin) == 0;
+ } else {
+ return false;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 SXFile::getLength() {
+ if (_mode == 1 && _readFile) {
+ return _readFile->size();
+ } else if ((_mode == 2 || _mode == 3) && _writeFile) {
+ error("SXFile - reading length for WriteFile not supported");
+ return 0;
+ /*
+ uint32 currentPos = ftell((FILE *)_writeFile);
+ fseek((FILE *)_writeFile, 0, SEEK_END);
+ int ret = ftell((FILE *)_writeFile);
+ fseek((FILE *)_writeFile, CurrentPos, SEEK_SET);
+ return Ret;*/
+ } else {
+ return 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SXFile::persist(BasePersistenceManager *persistMgr) {
+
+ BaseScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_filename));
+ persistMgr->transfer(TMEMBER(_mode));
+ persistMgr->transfer(TMEMBER(_textMode));
+
+ uint32 pos = 0;
+ if (persistMgr->getIsSaving()) {
+ pos = getPos();
+ persistMgr->transfer(TMEMBER(pos));
+ } else {
+ persistMgr->transfer(TMEMBER(pos));
+
+ // try to re-open file if needed
+ _writeFile = NULL;
+ _readFile = NULL;
+
+ if (_mode != 0) {
+ // open for reading
+ if (_mode == 1) {
+ _readFile = BaseFileManager::getEngineInstance()->openFile(_filename);
+ if (!_readFile) {
+ close();
+ }
+ }
+ // open for writing / appending
+ else {
+ if (_textMode) {
+ if (_mode == 2) {
+ _writeFile = openForWrite(_filename, false);
+ } else {
+ _writeFile = openForAppend(_filename, false);
+ }
+ } else {
+ if (_mode == 2) {
+ _writeFile = openForWrite(_filename, true);
+ } else {
+ _writeFile = openForAppend(_filename, true);
+ }
+ }
+ if (_writeFile) {
+ close();
+ }
+ }
+ setPos(pos);
+ }
+ }
+
+ return STATUS_OK;
+}
+
+// Should replace fopen(..., "wb+") and fopen(..., "w+")
+Common::WriteStream *SXFile::openForWrite(const Common::String &filename, bool binary) {
+ error("SXFile::openForWrite - WriteFiles not supported");
+}
+
+// Should replace fopen(..., "ab+") and fopen(..., "a+")
+Common::WriteStream *SXFile::openForAppend(const Common::String &filename, bool binary) {
+ error("SXFile::openForAppend - WriteFiles not supported");
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_ext_file.h b/engines/wintermute/base/scriptables/script_ext_file.h
new file mode 100644
index 0000000000..f7c72fcfb3
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_file.h
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTES_SXFILE_H
+#define WINTERMUTES_SXFILE_H
+
+
+#include "engines/wintermute/base/base_scriptable.h"
+#include "common/stream.h"
+
+namespace Wintermute {
+
+class BaseFile;
+
+class SXFile : public BaseScriptable {
+public:
+ DECLARE_PERSISTENT(SXFile, BaseScriptable)
+ ScValue *scGetProperty(const Common::String &name);
+ bool scSetProperty(const char *name, ScValue *value);
+ bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ const char *scToString();
+ SXFile(BaseGame *inGame, ScStack *Stack);
+ virtual ~SXFile();
+private:
+ Common::SeekableReadStream *_readFile;
+ Common::WriteStream *_writeFile;
+ int _mode; // 0..none, 1..read, 2..write, 3..append
+ bool _textMode;
+ void close();
+ void cleanup();
+ uint32 getPos();
+ uint32 getLength();
+ bool setPos(uint32 pos, int whence = SEEK_SET);
+ char *_filename;
+ Common::WriteStream *openForWrite(const Common::String &filename, bool binary);
+ Common::WriteStream *openForAppend(const Common::String &filename, bool binary);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_ext_math.cpp b/engines/wintermute/base/scriptables/script_ext_math.cpp
new file mode 100644
index 0000000000..d816fbec65
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_math.cpp
@@ -0,0 +1,295 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/script_ext_math.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/persistent.h"
+#include "common/math.h"
+#include <math.h>
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+IMPLEMENT_PERSISTENT(SXMath, true)
+
+BaseScriptable *makeSXMath(BaseGame *inGame) {
+ return new SXMath(inGame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXMath::SXMath(BaseGame *inGame) : BaseScriptable(inGame) {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SXMath::~SXMath() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXMath::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // Abs
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Abs") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(fabs(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Acos
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Acos") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(acos(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Asin
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Asin") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(asin(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Atan
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Atan") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(atan(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Atan2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Atan2") == 0) {
+ stack->correctParams(2);
+ double y = stack->pop()->getFloat();
+ double x = stack->pop()->getFloat();
+ stack->pushFloat(atan2(y, x));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Ceil
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Ceil") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(ceil(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Cos
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Cos") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(cos(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Cosh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Cosh") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(cosh(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Exp
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Exp") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(exp(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Floor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Floor") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(floor(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Log
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Log") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(log(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Log10
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Log10") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(log10(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Pow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Pow") == 0) {
+ stack->correctParams(2);
+ double x = stack->pop()->getFloat();
+ double y = stack->pop()->getFloat();
+
+ stack->pushFloat(pow(x, y));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sin
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Sin") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(sin(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sinh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Sinh") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(sinh(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Tan
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Tan") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(tan(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Tanh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Tanh") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(tanh(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sqrt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Sqrt") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(sqrt(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DegToRad
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DegToRad") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(degreeToRadian(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RadToDeg
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RadToDeg") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(radianToDegree(stack->pop()->getFloat()));
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *SXMath::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("math");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PI
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "PI") {
+ _scValue->setFloat(M_PI);
+ return _scValue;
+ } else {
+ return _scValue;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double SXMath::degreeToRadian(double value) {
+ return value * (M_PI / 180.0f);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double SXMath::radianToDegree(double value) {
+ return value * (180.0f / M_PI);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXMath::persist(BasePersistenceManager *persistMgr) {
+
+ BaseScriptable::persist(persistMgr);
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_ext_math.h b/engines/wintermute/base/scriptables/script_ext_math.h
new file mode 100644
index 0000000000..48c43ea7e8
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_math.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXMATH_H
+#define WINTERMUTE_SXMATH_H
+
+
+#include "engines/wintermute/base/base_scriptable.h"
+
+namespace Wintermute {
+
+class SXMath : public BaseScriptable {
+public:
+ DECLARE_PERSISTENT(SXMath, BaseScriptable)
+ SXMath(BaseGame *inGame);
+ virtual ~SXMath();
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+
+private:
+ double degreeToRadian(double value);
+ double radianToDegree(double value);
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp b/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp
new file mode 100644
index 0000000000..8f05b7bff6
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp
@@ -0,0 +1,529 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_scriptable.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_ext_mem_buffer.h"
+#include "common/file.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(SXMemBuffer, false)
+
+BaseScriptable *makeSXMemBuffer(BaseGame *inGame, ScStack *stack) {
+ return new SXMemBuffer(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXMemBuffer::SXMemBuffer(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
+ stack->correctParams(1);
+ _buffer = NULL;
+ _size = 0;
+
+ int newSize = stack->pop()->getInt();
+ resize(MAX(0, newSize));
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXMemBuffer::SXMemBuffer(BaseGame *inGame, void *buffer) : BaseScriptable(inGame) {
+ _size = 0;
+ _buffer = buffer;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SXMemBuffer::~SXMemBuffer() {
+ cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *SXMemBuffer::scToMemBuffer() {
+ return _buffer;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SXMemBuffer::cleanup() {
+ if (_size) {
+ free(_buffer);
+ }
+ _buffer = NULL;
+ _size = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SXMemBuffer::resize(int newSize) {
+ int oldSize = _size;
+
+ if (_size == 0) {
+ _buffer = malloc(newSize);
+ if (_buffer) {
+ _size = newSize;
+ }
+ } else {
+ void *newBuf = realloc(_buffer, newSize);
+ if (!newBuf) {
+ if (newSize == 0) {
+ _buffer = newBuf;
+ _size = newSize;
+ } else {
+ return STATUS_FAILED;
+ }
+ } else {
+ _buffer = newBuf;
+ _size = newSize;
+ }
+ }
+
+ if (_buffer && _size > oldSize) {
+ memset((byte *)_buffer + oldSize, 0, _size - oldSize);
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SXMemBuffer::checkBounds(ScScript *script, int start, int length) {
+ if (_buffer == NULL) {
+ script->runtimeError("Cannot use Set/Get methods on an uninitialized memory buffer");
+ return false;
+ }
+ if (_size == 0) {
+ return true;
+ }
+
+ if (start < 0 || length == 0 || start + length > _size) {
+ script->runtimeError("Set/Get method call is out of bounds");
+ return false;
+ } else {
+ return true;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *SXMemBuffer::scToString() {
+ return "[membuffer object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXMemBuffer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetSize
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetSize") == 0) {
+ stack->correctParams(1);
+ int newSize = stack->pop()->getInt();
+ newSize = MAX(0, newSize);
+ if (DID_SUCCEED(resize(newSize))) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetBool") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(bool))) {
+ stack->pushNULL();
+ } else {
+ stack->pushBool(*(bool *)((byte *)_buffer + start));
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetByte
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetByte") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(byte))) {
+ stack->pushNULL();
+ } else {
+ stack->pushInt(*(byte *)((byte *)_buffer + start));
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetShort") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(short))) {
+ stack->pushNULL();
+ } else {
+ stack->pushInt(65536 + * (short *)((byte *)_buffer + start));
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetInt / GetLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetInt") == 0 || strcmp(name, "GetLong") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(int))) {
+ stack->pushNULL();
+ } else {
+ stack->pushInt(*(int *)((byte *)_buffer + start));
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFloat") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(float))) {
+ stack->pushNULL();
+ } else {
+ stack->pushFloat(*(float *)((byte *)_buffer + start));
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetDouble") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(double))) {
+ stack->pushNULL();
+ } else {
+ stack->pushFloat(*(double *)((byte *)_buffer + start));
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetString") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ int length = stack->pop()->getInt();
+
+ // find end of string
+ if (length == 0 && start >= 0 && start < _size) {
+ for (int i = start; i < _size; i++) {
+ if (((char *)_buffer)[i] == '\0') {
+ length = i - start;
+ break;
+ }
+ }
+ }
+
+ if (!checkBounds(script, start, length)) {
+ stack->pushNULL();
+ } else {
+ char *str = new char[length + 1];
+ Common::strlcpy(str, (const char *)_buffer + start, length + 1);
+ stack->pushString(str);
+ delete[] str;
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetPointer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetPointer") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(void *))) {
+ stack->pushNULL();
+ } else {
+ void *pointer = *(void **)((byte *)_buffer + start);
+ SXMemBuffer *buf = new SXMemBuffer(_gameRef, pointer);
+ stack->pushNative(buf, false);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetBool") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ bool val = stack->pop()->getBool();
+
+ if (!checkBounds(script, start, sizeof(bool))) {
+ stack->pushBool(false);
+ } else {
+ *(bool *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetByte
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetByte") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ byte val = (byte)stack->pop()->getInt();
+
+ if (!checkBounds(script, start, sizeof(byte))) {
+ stack->pushBool(false);
+ } else {
+ *(byte *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetShort") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ short val = (short)stack->pop()->getInt();
+
+ if (!checkBounds(script, start, sizeof(short))) {
+ stack->pushBool(false);
+ } else {
+ *(short *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetInt / SetLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetInt") == 0 || strcmp(name, "SetLong") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ int val = stack->pop()->getInt();
+
+ if (!checkBounds(script, start, sizeof(int))) {
+ stack->pushBool(false);
+ } else {
+ *(int *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetFloat") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ float val = (float)stack->pop()->getFloat();
+
+ if (!checkBounds(script, start, sizeof(float))) {
+ stack->pushBool(false);
+ } else {
+ *(float *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetDouble") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ double val = stack->pop()->getFloat();
+
+ if (!checkBounds(script, start, sizeof(double))) {
+ stack->pushBool(false);
+ } else {
+ *(double *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetString") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ const char *val = stack->pop()->getString();
+
+ if (!checkBounds(script, start, strlen(val) + 1)) {
+ stack->pushBool(false);
+ } else {
+ memcpy((byte *)_buffer + start, val, strlen(val) + 1);
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetPointer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetPointer") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ /* ScValue *val = */ stack->pop();
+
+ if (!checkBounds(script, start, sizeof(void *))) {
+ stack->pushBool(false);
+ } else {
+ /*
+ int pointer = (int)Val->getMemBuffer();
+ memcpy((byte *)_buffer+Start, &Pointer, sizeof(void*));
+ stack->pushBool(true);
+ */
+ // TODO fix
+ stack->pushBool(false);
+
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DEBUG_Dump
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DEBUG_Dump") == 0) {
+ stack->correctParams(0);
+ if (_buffer && _size) {
+ warning("SXMemBuffer::ScCallMethod - DEBUG_Dump");
+ Common::DumpFile f;
+ f.open("buffer.bin");
+ f.write(_buffer, _size);
+ f.close();
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *SXMemBuffer::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("membuffer");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Size (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Size") {
+ _scValue->setInt(_size);
+ return _scValue;
+ } else {
+ return BaseScriptable::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXMemBuffer::scSetProperty(const char *name, ScValue *value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Length")==0){
+ int origLength = _length;
+ _length = max(value->getInt(0), 0);
+
+ char propName[20];
+ if (_length < OrigLength){
+ for(int i=_length; i<OrigLength; i++){
+ sprintf(PropName, "%d", i);
+ _values->DeleteProp(PropName);
+ }
+ }
+ return STATUS_OK;
+ }
+ else*/ return BaseScriptable::scSetProperty(name, value);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXMemBuffer::persist(BasePersistenceManager *persistMgr) {
+
+ BaseScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_size));
+
+ if (persistMgr->getIsSaving()) {
+ if (_size > 0) {
+ persistMgr->putBytes((byte *)_buffer, _size);
+ }
+ } else {
+ if (_size > 0) {
+ _buffer = malloc(_size);
+ persistMgr->getBytes((byte *)_buffer, _size);
+ } else {
+ _buffer = NULL;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int SXMemBuffer::scCompare(BaseScriptable *val) {
+ if (_buffer == val->scToMemBuffer()) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_ext_mem_buffer.h b/engines/wintermute/base/scriptables/script_ext_mem_buffer.h
new file mode 100644
index 0000000000..1527a323dc
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_mem_buffer.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXMEMBUFFER_H
+#define WINTERMUTE_SXMEMBUFFER_H
+
+
+#include "engines/wintermute/base/base_scriptable.h"
+
+namespace Wintermute {
+
+class SXMemBuffer : public BaseScriptable {
+public:
+ virtual int scCompare(BaseScriptable *Val);
+ DECLARE_PERSISTENT(SXMemBuffer, BaseScriptable)
+ ScValue *scGetProperty(const Common::String &name);
+ bool scSetProperty(const char *name, ScValue *value);
+ bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ const char *scToString();
+ SXMemBuffer(BaseGame *inGame, ScStack *stack);
+ SXMemBuffer(BaseGame *inGame, void *buffer);
+ virtual ~SXMemBuffer();
+ virtual void *scToMemBuffer();
+private:
+ int _size;
+
+ bool resize(int newSize);
+ void *_buffer;
+ void cleanup();
+ bool checkBounds(ScScript *script, int start, int length);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_ext_object.cpp b/engines/wintermute/base/scriptables/script_ext_object.cpp
new file mode 100644
index 0000000000..b87aac81f9
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_object.cpp
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/script_ext_object.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(SXObject, false)
+
+BaseScriptable *makeSXObject(BaseGame *inGame, ScStack *stack) {
+ return new SXObject(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXObject::SXObject(BaseGame *inGame, ScStack *stack) : BaseObject(inGame) {
+ int numParams = stack->pop()->getInt(0);
+ for (int i = 0; i < numParams; i++) {
+ addScript(stack->pop()->getString());
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SXObject::~SXObject() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXObject::persist(BasePersistenceManager *persistMgr) {
+ BaseObject::persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_ext_object.h b/engines/wintermute/base/scriptables/script_ext_object.h
new file mode 100644
index 0000000000..c85d16d44e
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_object.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXOBJECT_H
+#define WINTERMUTE_SXOBJECT_H
+
+
+#include "engines/wintermute/base/base_object.h"
+
+namespace Wintermute {
+
+class SXObject : public BaseObject {
+public:
+ DECLARE_PERSISTENT(SXObject, BaseObject)
+ SXObject(BaseGame *inGame, ScStack *Stack);
+ virtual ~SXObject();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_ext_string.cpp b/engines/wintermute/base/scriptables/script_ext_string.cpp
new file mode 100644
index 0000000000..5f7da1c2dd
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_string.cpp
@@ -0,0 +1,436 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/base/scriptables/script_ext_string.h"
+#include "engines/wintermute/base/scriptables/script_ext_array.h"
+#include "engines/wintermute/utils/string_util.h"
+#include "common/tokenizer.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(SXString, false)
+
+BaseScriptable *makeSXString(BaseGame *inGame, ScStack *stack) {
+ return new SXString(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+SXString::SXString(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
+ _string = NULL;
+ _capacity = 0;
+
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ if (val->isInt()) {
+ _capacity = MAX(0, val->getInt());
+ if (_capacity > 0) {
+ _string = new char[_capacity];
+ memset(_string, 0, _capacity);
+ }
+ } else {
+ setStringVal(val->getString());
+ }
+
+ if (_capacity == 0) {
+ setStringVal("");
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SXString::~SXString() {
+ if (_string) {
+ delete[] _string;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void SXString::setStringVal(const char *val) {
+ int len = strlen(val);
+ if (len >= _capacity) {
+ _capacity = len + 1;
+ delete[] _string;
+ _string = NULL;
+ _string = new char[_capacity];
+ memset(_string, 0, _capacity);
+ }
+ strcpy(_string, val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *SXString::scToString() {
+ if (_string) {
+ return _string;
+ } else {
+ return "[null string]";
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void SXString::scSetString(const char *val) {
+ setStringVal(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXString::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // Substring
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Substring") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ int end = stack->pop()->getInt();
+
+ if (end < start) {
+ BaseUtils::swap(&start, &end);
+ }
+
+ //try {
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ str = StringUtil::utf8ToWide(_string);
+ } else {
+ str = StringUtil::ansiToWide(_string);
+ }
+
+ //WideString subStr = str.substr(start, end - start + 1);
+ WideString subStr(str.c_str() + start, end - start + 1);
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ stack->pushString(StringUtil::wideToUtf8(subStr).c_str());
+ } else {
+ stack->pushString(StringUtil::wideToAnsi(subStr).c_str());
+ }
+ // } catch (std::exception &) {
+ // stack->pushNULL();
+ // }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Substr
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Substr") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+
+ ScValue *val = stack->pop();
+ int len = val->getInt();
+
+ if (!val->isNULL() && len <= 0) {
+ stack->pushString("");
+ return STATUS_OK;
+ }
+
+ if (val->isNULL()) {
+ len = strlen(_string) - start;
+ }
+
+// try {
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ str = StringUtil::utf8ToWide(_string);
+ } else {
+ str = StringUtil::ansiToWide(_string);
+ }
+
+// WideString subStr = str.substr(start, len);
+ WideString subStr(str.c_str() + start, len);
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ stack->pushString(StringUtil::wideToUtf8(subStr).c_str());
+ } else {
+ stack->pushString(StringUtil::wideToAnsi(subStr).c_str());
+ }
+// } catch (std::exception &) {
+// stack->pushNULL();
+// }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToUpperCase
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ToUpperCase") == 0) {
+ stack->correctParams(0);
+
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ str = StringUtil::utf8ToWide(_string);
+ } else {
+ str = StringUtil::ansiToWide(_string);
+ }
+
+ str.toUppercase();
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ stack->pushString(StringUtil::wideToUtf8(str).c_str());
+ } else {
+ stack->pushString(StringUtil::wideToAnsi(str).c_str());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToLowerCase
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ToLowerCase") == 0) {
+ stack->correctParams(0);
+
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ str = StringUtil::utf8ToWide(_string);
+ } else {
+ str = StringUtil::ansiToWide(_string);
+ }
+
+ str.toLowercase();
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ stack->pushString(StringUtil::wideToUtf8(str).c_str());
+ } else {
+ stack->pushString(StringUtil::wideToAnsi(str).c_str());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IndexOf
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IndexOf") == 0) {
+ stack->correctParams(2);
+
+ const char *strToFind = stack->pop()->getString();
+ int index = stack->pop()->getInt();
+
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ str = StringUtil::utf8ToWide(_string);
+ } else {
+ str = StringUtil::ansiToWide(_string);
+ }
+
+ WideString toFind;
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ toFind = StringUtil::utf8ToWide(strToFind);
+ } else {
+ toFind = StringUtil::ansiToWide(strToFind);
+ }
+
+ int indexOf = StringUtil::indexOf(str, toFind, index);
+ stack->pushInt(indexOf);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Split
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Split") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ char separators[MAX_PATH_LENGTH] = ",";
+ if (!val->isNULL()) {
+ strcpy(separators, val->getString());
+ }
+
+ SXArray *array = new SXArray(_gameRef);
+ if (!array) {
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ str = StringUtil::utf8ToWide(_string);
+ } else {
+ str = StringUtil::ansiToWide(_string);
+ }
+
+ WideString delims;
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ delims = StringUtil::utf8ToWide(separators);
+ } else {
+ delims = StringUtil::ansiToWide(separators);
+ }
+
+ Common::Array<WideString> parts;
+
+
+
+ Common::StringTokenizer tokenizer(str, delims);
+ while (!tokenizer.empty()) {
+ Common::String str2 = tokenizer.nextToken();
+ parts.push_back(str2);
+ }
+ // TODO: Clean this up
+ /*do {
+ pos = StringUtil::IndexOf(Common::String(str.c_str() + start), delims, start);
+ //pos = str.find_first_of(delims, start);
+ if (pos == start) {
+ start = pos + 1;
+ } else if (pos == str.size()) {
+ parts.push_back(Common::String(str.c_str() + start));
+ break;
+ } else {
+ parts.push_back(Common::String(str.c_str() + start, pos - start));
+ start = pos + 1;
+ }
+ //start = str.find_first_not_of(delims, start);
+ start = StringUtil::LastIndexOf(Common::String(str.c_str() + start), delims, start) + 1;
+
+ } while (pos != str.size());*/
+
+ for (Common::Array<WideString>::iterator it = parts.begin(); it != parts.end(); ++it) {
+ WideString &part = (*it);
+
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ val = new ScValue(_gameRef, StringUtil::wideToUtf8(part).c_str());
+ } else {
+ val = new ScValue(_gameRef, StringUtil::wideToAnsi(part).c_str());
+ }
+
+ array->push(val);
+ delete val;
+ val = NULL;
+ }
+
+ stack->pushNative(array, false);
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *SXString::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("string");
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Length (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Length") {
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ WideString wstr = StringUtil::utf8ToWide(_string);
+ _scValue->setInt(wstr.size());
+ } else {
+ _scValue->setInt(strlen(_string));
+ }
+
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Capacity
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Capacity") {
+ _scValue->setInt(_capacity);
+ return _scValue;
+ } else {
+ return _scValue;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXString::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Capacity
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Capacity") == 0) {
+ int32 newCap = (uint32)value->getInt();
+ if (newCap < (int32)(strlen(_string) + 1)) {
+ _gameRef->LOG(0, "Warning: cannot lower string capacity");
+ } else if (newCap != _capacity) {
+ char *newStr = new char[newCap];
+ if (newStr) {
+ memset(newStr, 0, newCap);
+ strcpy(newStr, _string);
+ delete[] _string;
+ _string = newStr;
+ _capacity = newCap;
+ }
+ }
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SXString::persist(BasePersistenceManager *persistMgr) {
+
+ BaseScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_capacity));
+
+ if (persistMgr->getIsSaving()) {
+ if (_capacity > 0) {
+ persistMgr->putBytes((byte *)_string, _capacity);
+ }
+ } else {
+ if (_capacity > 0) {
+ _string = new char[_capacity];
+ persistMgr->getBytes((byte *)_string, _capacity);
+ } else {
+ _string = NULL;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int SXString::scCompare(BaseScriptable *val) {
+ return strcmp(_string, ((SXString *)val)->_string);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_ext_string.h b/engines/wintermute/base/scriptables/script_ext_string.h
new file mode 100644
index 0000000000..00bffab3a9
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_ext_string.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXSTRING_H
+#define WINTERMUTE_SXSTRING_H
+
+
+#include "engines/wintermute/base/base_scriptable.h"
+
+namespace Wintermute {
+
+class SXString : public BaseScriptable {
+public:
+ virtual int scCompare(BaseScriptable *Val);
+ DECLARE_PERSISTENT(SXString, BaseScriptable)
+ ScValue *scGetProperty(const Common::String &name);
+ bool scSetProperty(const char *name, ScValue *value);
+ bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ void scSetString(const char *val);
+ const char *scToString();
+ void setStringVal(const char *val);
+
+ SXString(BaseGame *inGame, ScStack *Stack);
+ virtual ~SXString();
+
+private:
+ char *_string;
+ int _capacity;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_stack.cpp b/engines/wintermute/base/scriptables/script_stack.cpp
new file mode 100644
index 0000000000..77367045c2
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_stack.cpp
@@ -0,0 +1,232 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/base_game.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(ScStack, false)
+
+//////////////////////////////////////////////////////////////////////////
+ScStack::ScStack(BaseGame *inGame) : BaseClass(inGame) {
+ _sP = -1;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScStack::~ScStack() {
+
+#if _DEBUG
+ //_gameRef->LOG(0, "STAT: Stack size: %d, SP=%d", _values.size(), _sP);
+#endif
+
+ for (uint32 i = 0; i < _values.size(); i++) {
+ delete _values[i];
+ }
+ _values.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *ScStack::pop() {
+ if (_sP < 0) {
+ _gameRef->LOG(0, "Fatal: Stack underflow");
+ return NULL;
+ }
+
+ return _values[_sP--];
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScStack::push(ScValue *val) {
+ _sP++;
+
+ if (_sP < (int32)_values.size()) {
+ _values[_sP]->cleanup();
+ _values[_sP]->copy(val);
+ } else {
+ ScValue *copyVal = new ScValue(_gameRef);
+ copyVal->copy(val);
+ _values.add(copyVal);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *ScStack::getPushValue() {
+ _sP++;
+
+ if (_sP >= (int32)_values.size()) {
+ ScValue *val = new ScValue(_gameRef);
+ _values.add(val);
+ }
+ _values[_sP]->cleanup();
+ return _values[_sP];
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *ScStack::getTop() {
+ if (_sP < 0 || _sP >= (int32)_values.size()) {
+ return NULL;
+ } else {
+ return _values[_sP];
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *ScStack::getAt(int index) {
+ index = _sP - index;
+ if (index < 0 || index >= (int32)_values.size()) {
+ return NULL;
+ } else {
+ return _values[index];
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScStack::correctParams(uint32 expectedParams) {
+ uint32 nuParams = (uint32)pop()->getInt();
+
+ if (expectedParams < nuParams) { // too many params
+ while (expectedParams < nuParams) {
+ //Pop();
+ delete _values[_sP - expectedParams];
+ _values.remove_at(_sP - expectedParams);
+ nuParams--;
+ _sP--;
+ }
+ } else if (expectedParams > nuParams) { // need more params
+ while (expectedParams > nuParams) {
+ //Push(null_val);
+ ScValue *nullVal = new ScValue(_gameRef);
+ nullVal->setNULL();
+ _values.insert_at(_sP - nuParams + 1, nullVal);
+ nuParams++;
+ _sP++;
+
+ if ((int32)_values.size() > _sP + 1) {
+ delete _values[_values.size() - 1];
+ _values.remove_at(_values.size() - 1);
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScStack::pushNULL() {
+ /*
+ ScValue* val = new ScValue(_gameRef);
+ val->setNULL();
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setNULL();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScStack::pushInt(int val) {
+ /*
+ ScValue* val = new ScValue(_gameRef);
+ val->setInt(Val);
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setInt(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScStack::pushFloat(double val) {
+ /*
+ ScValue* val = new ScValue(_gameRef);
+ val->setFloat(Val);
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setFloat(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScStack::pushBool(bool val) {
+ /*
+ ScValue* val = new ScValue(_gameRef);
+ val->setBool(Val);
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setBool(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScStack::pushString(const char *val) {
+ /*
+ ScValue* val = new ScValue(_gameRef);
+ val->setString(Val);
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setString(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScStack::pushNative(BaseScriptable *val, bool persistent) {
+ /*
+ ScValue* val = new ScValue(_gameRef);
+ val->setNative(Val, Persistent);
+ Push(val);
+ delete val;
+ */
+
+ getPushValue()->setNative(val, persistent);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScStack::persist(BasePersistenceManager *persistMgr) {
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER(_sP));
+ _values.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_stack.h b/engines/wintermute/base/scriptables/script_stack.h
new file mode 100644
index 0000000000..86d246cf34
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_stack.h
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SCSTACK_H
+#define WINTERMUTE_SCSTACK_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/persistent.h"
+
+namespace Wintermute {
+
+class ScValue;
+class BaseScriptable;
+
+class ScStack : public BaseClass {
+public:
+ ScValue *getAt(int Index);
+ ScValue *getPushValue();
+ DECLARE_PERSISTENT(ScStack, BaseClass)
+ void pushNative(BaseScriptable *val, bool persistent);
+ void pushString(const char *val);
+ void pushBool(bool val);
+ void pushInt(int val);
+ void pushFloat(double val);
+ void pushNULL();
+ void correctParams(uint32 expectedParams);
+ ScValue *getTop();
+ void push(ScValue *val);
+ ScValue *pop();
+ ScStack(BaseGame *inGame);
+ virtual ~ScStack();
+ BaseArray<ScValue *> _values;
+ int _sP;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/script_value.cpp b/engines/wintermute/base/scriptables/script_value.cpp
new file mode 100644
index 0000000000..0bc7ab5807
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_value.cpp
@@ -0,0 +1,995 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/utils/string_util.h"
+#include "engines/wintermute/base/base_scriptable.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(ScValue, false)
+
+//////////////////////////////////////////////////////////////////////////
+ScValue::ScValue(BaseGame *inGame) : BaseClass(inGame) {
+ _type = VAL_NULL;
+
+ _valBool = false;
+ _valInt = 0;
+ _valFloat = 0.0f;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue::ScValue(BaseGame *inGame, bool val) : BaseClass(inGame) {
+ _type = VAL_BOOL;
+ _valBool = val;
+
+ _valInt = 0;
+ _valFloat = 0.0f;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue::ScValue(BaseGame *inGame, int val) : BaseClass(inGame) {
+ _type = VAL_INT;
+ _valInt = val;
+
+ _valFloat = 0.0f;
+ _valBool = false;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue::ScValue(BaseGame *inGame, double val) : BaseClass(inGame) {
+ _type = VAL_FLOAT;
+ _valFloat = val;
+
+ _valInt = 0;
+ _valBool = false;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue::ScValue(BaseGame *inGame, const char *val) : BaseClass(inGame) {
+ _type = VAL_STRING;
+ _valString = NULL;
+ setStringVal(val);
+
+ _valBool = false;
+ _valInt = 0;
+ _valFloat = 0.0f;
+ _valNative = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::cleanup(bool ignoreNatives) {
+ deleteProps();
+
+ if (_valString) {
+ delete[] _valString;
+ }
+
+ if (!ignoreNatives) {
+ if (_valNative && !_persistent) {
+ _valNative->_refCount--;
+ if (_valNative->_refCount <= 0) {
+ delete _valNative;
+ _valNative = NULL;
+ }
+ }
+ }
+
+
+ _type = VAL_NULL;
+
+ _valBool = false;
+ _valInt = 0;
+ _valFloat = 0.0f;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue::~ScValue() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *ScValue::getProp(const char *name) {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->getProp(name);
+ }
+
+ if (_type == VAL_STRING && strcmp(name, "Length") == 0) {
+ _gameRef->_scValue->_type = VAL_INT;
+
+ if (_gameRef->_textEncoding == TEXT_ANSI) {
+ _gameRef->_scValue->setInt(strlen(_valString));
+ } else {
+ WideString wstr = StringUtil::utf8ToWide(_valString);
+ _gameRef->_scValue->setInt(wstr.size());
+ }
+
+ return _gameRef->_scValue;
+ }
+
+ ScValue *ret = NULL;
+
+ if (_type == VAL_NATIVE && _valNative) {
+ ret = _valNative->scGetProperty(name);
+ }
+
+ if (ret == NULL) {
+ _valIter = _valObject.find(name);
+ if (_valIter != _valObject.end()) {
+ ret = _valIter->_value;
+ }
+ }
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::deleteProp(const char *name) {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->deleteProp(name);
+ }
+
+ _valIter = _valObject.find(name);
+ if (_valIter != _valObject.end()) {
+ delete _valIter->_value;
+ _valIter->_value = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::setProp(const char *name, ScValue *val, bool copyWhole, bool setAsConst) {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->setProp(name, val);
+ }
+
+ bool ret = STATUS_FAILED;
+ if (_type == VAL_NATIVE && _valNative) {
+ ret = _valNative->scSetProperty(name, val);
+ }
+
+ if (DID_FAIL(ret)) {
+ ScValue *newVal = NULL;
+
+ _valIter = _valObject.find(name);
+ if (_valIter != _valObject.end()) {
+ newVal = _valIter->_value;
+ }
+ if (!newVal) {
+ newVal = new ScValue(_gameRef);
+ } else {
+ newVal->cleanup();
+ }
+
+ newVal->copy(val, copyWhole);
+ newVal->_isConstVar = setAsConst;
+ _valObject[name] = newVal;
+
+ if (_type != VAL_NATIVE) {
+ _type = VAL_OBJECT;
+ }
+
+ /*
+ _valIter = _valObject.find(Name);
+ if (_valIter != _valObject.end()){
+ delete _valIter->_value;
+ _valIter->_value = NULL;
+ }
+ ScValue* val = new ScValue(_gameRef);
+ val->Copy(Val, CopyWhole);
+ val->_isConstVar = SetAsConst;
+ _valObject[Name] = val;
+
+ if (_type!=VAL_NATIVE) _type = VAL_OBJECT;
+ */
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::propExists(const char *name) {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->propExists(name);
+ }
+ _valIter = _valObject.find(name);
+
+ return (_valIter != _valObject.end());
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::deleteProps() {
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ delete(ScValue *)_valIter->_value;
+ _valIter++;
+ }
+ _valObject.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::CleanProps(bool includingNatives) {
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ if (!_valIter->_value->_isConstVar && (!_valIter->_value->isNative() || includingNatives)) {
+ _valIter->_value->setNULL();
+ }
+ _valIter++;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::isNULL() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->isNULL();
+ }
+
+ return (_type == VAL_NULL);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::isNative() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->isNative();
+ }
+
+ return (_type == VAL_NATIVE);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::isString() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->isString();
+ }
+
+ return (_type == VAL_STRING);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::isFloat() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->isFloat();
+ }
+
+ return (_type == VAL_FLOAT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::isInt() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->isInt();
+ }
+
+ return (_type == VAL_INT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::isBool() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->isBool();
+ }
+
+ return (_type == VAL_BOOL);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::isObject() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->isObject();
+ }
+
+ return (_type == VAL_OBJECT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TValType ScValue::getTypeTolerant() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->getType();
+ }
+
+ return _type;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setBool(bool val) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setBool(val);
+ return;
+ }
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scSetBool(val);
+ return;
+ }
+
+ _valBool = val;
+ _type = VAL_BOOL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setInt(int val) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setInt(val);
+ return;
+ }
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scSetInt(val);
+ return;
+ }
+
+ _valInt = val;
+ _type = VAL_INT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setFloat(double val) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setFloat(val);
+ return;
+ }
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scSetFloat(val);
+ return;
+ }
+
+ _valFloat = val;
+ _type = VAL_FLOAT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setString(const char *val) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setString(val);
+ return;
+ }
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scSetString(val);
+ return;
+ }
+
+ setStringVal(val);
+ if (_valString) {
+ _type = VAL_STRING;
+ } else {
+ _type = VAL_NULL;
+ }
+}
+
+void ScValue::setString(const Common::String &val) {
+ setString(val.c_str());
+}
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setStringVal(const char *val) {
+ if (_valString) {
+ delete[] _valString;
+ _valString = NULL;
+ }
+
+ if (val == NULL) {
+ _valString = NULL;
+ return;
+ }
+
+ _valString = new char [strlen(val) + 1];
+ if (_valString) {
+ strcpy(_valString, val);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setNULL() {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setNULL();
+ return;
+ }
+
+ if (_valNative && !_persistent) {
+ _valNative->_refCount--;
+ if (_valNative->_refCount <= 0) {
+ delete _valNative;
+ }
+ }
+ _valNative = NULL;
+ deleteProps();
+
+ _type = VAL_NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setNative(BaseScriptable *val, bool persistent) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setNative(val, persistent);
+ return;
+ }
+
+ if (val == NULL) {
+ setNULL();
+ } else {
+ if (_valNative && !_persistent) {
+ _valNative->_refCount--;
+ if (_valNative->_refCount <= 0) {
+ if (_valNative != val) {
+ delete _valNative;
+ }
+ _valNative = NULL;
+ }
+ }
+
+ _type = VAL_NATIVE;
+ _persistent = persistent;
+
+ _valNative = val;
+ if (_valNative && !_persistent) {
+ _valNative->_refCount++;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setObject() {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setObject();
+ return;
+ }
+
+ deleteProps();
+ _type = VAL_OBJECT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setReference(ScValue *val) {
+ _valRef = val;
+ _type = VAL_VARIABLE_REF;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::getBool(bool defaultVal) {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->getBool();
+ }
+
+ switch (_type) {
+ case VAL_BOOL:
+ return _valBool;
+
+ case VAL_NATIVE:
+ return _valNative->scToBool();
+
+ case VAL_INT:
+ return (_valInt != 0);
+
+ case VAL_FLOAT:
+ return (_valFloat != 0.0f);
+
+ case VAL_STRING:
+ return (scumm_stricmp(_valString, "1") == 0 || scumm_stricmp(_valString, "yes") == 0 || scumm_stricmp(_valString, "true") == 0);
+
+ default:
+ return defaultVal;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int ScValue::getInt(int defaultVal) {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->getInt();
+ }
+
+ switch (_type) {
+ case VAL_BOOL:
+ return _valBool ? 1 : 0;
+
+ case VAL_NATIVE:
+ return _valNative->scToInt();
+
+ case VAL_INT:
+ return _valInt;
+
+ case VAL_FLOAT:
+ return (int)_valFloat;
+
+ case VAL_STRING:
+ return atoi(_valString);
+
+ default:
+ return defaultVal;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double ScValue::getFloat(double defaultVal) {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->getFloat();
+ }
+
+ switch (_type) {
+ case VAL_BOOL:
+ return _valBool ? 1.0f : 0.0f;
+
+ case VAL_NATIVE:
+ return _valNative->scToFloat();
+
+ case VAL_INT:
+ return (double)_valInt;
+
+ case VAL_FLOAT:
+ return _valFloat;
+
+ case VAL_STRING:
+ return atof(_valString);
+
+ default:
+ return defaultVal;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *ScValue::getMemBuffer() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->getMemBuffer();
+ }
+
+ if (_type == VAL_NATIVE) {
+ return _valNative->scToMemBuffer();
+ } else {
+ return (void *)NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *ScValue::getString() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->getString();
+ }
+
+ switch (_type) {
+ case VAL_OBJECT:
+ setStringVal("[object]");
+ break;
+
+ case VAL_NULL:
+ setStringVal("[null]");
+ break;
+
+ case VAL_NATIVE: {
+ const char *strVal = _valNative->scToString();
+ setStringVal(strVal);
+ return strVal;
+ break;
+ }
+
+ case VAL_BOOL:
+ setStringVal(_valBool ? "yes" : "no");
+ break;
+
+ case VAL_INT: {
+ char dummy[50];
+ sprintf(dummy, "%d", _valInt);
+ setStringVal(dummy);
+ break;
+ }
+
+ case VAL_FLOAT: {
+ char dummy[50];
+ sprintf(dummy, "%f", _valFloat);
+ setStringVal(dummy);
+ break;
+ }
+
+ case VAL_STRING:
+ break;
+
+ default:
+ setStringVal("");
+ }
+
+ return _valString;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseScriptable *ScValue::getNative() {
+ if (_type == VAL_VARIABLE_REF) {
+ return _valRef->getNative();
+ }
+
+ if (_type == VAL_NATIVE) {
+ return _valNative;
+ } else {
+ return NULL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TValType ScValue::getType() {
+ return _type;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::copy(ScValue *orig, bool copyWhole) {
+ _gameRef = orig->_gameRef;
+
+ if (_valNative && !_persistent) {
+ _valNative->_refCount--;
+ if (_valNative->_refCount <= 0) {
+ if (_valNative != orig->_valNative) {
+ delete _valNative;
+ }
+ _valNative = NULL;
+ }
+ }
+
+ if (orig->_type == VAL_VARIABLE_REF && orig->_valRef && copyWhole) {
+ orig = orig->_valRef;
+ }
+
+ cleanup(true);
+
+ _type = orig->_type;
+ _valBool = orig->_valBool;
+ _valInt = orig->_valInt;
+ _valFloat = orig->_valFloat;
+ setStringVal(orig->_valString);
+
+ _valRef = orig->_valRef;
+ _persistent = orig->_persistent;
+
+ _valNative = orig->_valNative;
+ if (_valNative && !_persistent) {
+ _valNative->_refCount++;
+ }
+//!!!! ref->native++
+
+ // copy properties
+ if (orig->_type == VAL_OBJECT && orig->_valObject.size() > 0) {
+ orig->_valIter = orig->_valObject.begin();
+ while (orig->_valIter != orig->_valObject.end()) {
+ _valObject[orig->_valIter->_key] = new ScValue(_gameRef);
+ _valObject[orig->_valIter->_key]->copy(orig->_valIter->_value);
+ orig->_valIter++;
+ }
+ } else {
+ _valObject.clear();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void ScValue::setValue(ScValue *val) {
+ if (val->_type == VAL_VARIABLE_REF) {
+ setValue(val->_valRef);
+ return;
+ }
+
+ // if being assigned a simple type, preserve native state
+ if (_type == VAL_NATIVE && (val->_type == VAL_INT || val->_type == VAL_STRING || val->_type == VAL_BOOL)) {
+ switch (val->_type) {
+ case VAL_INT:
+ _valNative->scSetInt(val->getInt());
+ break;
+ case VAL_FLOAT:
+ _valNative->scSetFloat(val->getFloat());
+ break;
+ case VAL_BOOL:
+ _valNative->scSetBool(val->getBool());
+ break;
+ case VAL_STRING:
+ _valNative->scSetString(val->getString());
+ break;
+ default:
+ warning("ScValue::setValue - unhandled enum");
+ break;
+ }
+ }
+ // otherwise just copy everything
+ else {
+ copy(val);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::persist(BasePersistenceManager *persistMgr) {
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER(_persistent));
+ persistMgr->transfer(TMEMBER(_isConstVar));
+ persistMgr->transfer(TMEMBER_INT(_type));
+ persistMgr->transfer(TMEMBER(_valBool));
+ persistMgr->transfer(TMEMBER(_valFloat));
+ persistMgr->transfer(TMEMBER(_valInt));
+ persistMgr->transfer(TMEMBER(_valNative));
+
+ int size;
+ const char *str;
+ if (persistMgr->getIsSaving()) {
+ size = _valObject.size();
+ persistMgr->transfer("", &size);
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ str = _valIter->_key.c_str();
+ persistMgr->transfer("", &str);
+ persistMgr->transfer("", &_valIter->_value);
+
+ _valIter++;
+ }
+ } else {
+ ScValue *val;
+ persistMgr->transfer("", &size);
+ for (int i = 0; i < size; i++) {
+ persistMgr->transfer("", &str);
+ persistMgr->transfer("", &val);
+
+ _valObject[str] = val;
+ delete[] str;
+ }
+ }
+
+ persistMgr->transfer(TMEMBER(_valRef));
+ persistMgr->transfer(TMEMBER(_valString));
+
+ /*
+ FILE* f = fopen("c:\\val.log", "a+");
+ switch(_type)
+ {
+ case VAL_STRING:
+ fprintf(f, "str %s\n", _valString);
+ break;
+
+ case VAL_INT:
+ fprintf(f, "int %d\n", _valInt);
+ break;
+
+ case VAL_BOOL:
+ fprintf(f, "bool %d\n", _valBool);
+ break;
+
+ case VAL_NULL:
+ fprintf(f, "null\n");
+ break;
+
+ case VAL_NATIVE:
+ fprintf(f, "native\n");
+ break;
+
+ case VAL_VARIABLE_REF:
+ fprintf(f, "ref\n");
+ break;
+
+ case VAL_OBJECT:
+ fprintf(f, "obj\n");
+ break;
+
+ case VAL_FLOAT:
+ fprintf(f, "float\n");
+ break;
+
+ }
+ fclose(f);
+ */
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ buffer->putTextIndent(indent, "PROPERTY {\n");
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", _valIter->_key.c_str());
+ buffer->putTextIndent(indent + 2, "VALUE=\"%s\"\n", _valIter->_value->getString());
+ buffer->putTextIndent(indent, "}\n\n");
+
+ _valIter++;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// -1 ... left is less, 0 ... equals, 1 ... left is greater
+int ScValue::compare(ScValue *val1, ScValue *val2) {
+ // both natives?
+ if (val1->isNative() && val2->isNative()) {
+ // same class?
+ if (strcmp(val1->getNative()->getClassName(), val2->getNative()->getClassName()) == 0) {
+ return val1->getNative()->scCompare(val2->getNative());
+ } else {
+ return strcmp(val1->getString(), val2->getString());
+ }
+ }
+
+ // both objects?
+ if (val1->isObject() && val2->isObject()) {
+ return -1;
+ }
+
+
+ // null states
+ if (val1->isNULL() && !val2->isNULL()) {
+ return -1;
+ } else if (!val1->isNULL() && val2->isNULL()) {
+ return 1;
+ } else if (val1->isNULL() && val2->isNULL()) {
+ return 0;
+ }
+
+ // one of them is string? convert both to string
+ if (val1->isString() || val2->isString()) {
+ return strcmp(val1->getString(), val2->getString());
+ }
+
+ // one of them is float?
+ if (val1->isFloat() || val2->isFloat()) {
+ if (val1->getFloat() < val2->getFloat()) {
+ return -1;
+ } else if (val1->getFloat() > val2->getFloat()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ // otherwise compare as int's
+ if (val1->getInt() < val2->getInt()) {
+ return -1;
+ } else if (val1->getInt() > val2->getInt()) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int ScValue::compareStrict(ScValue *val1, ScValue *val2) {
+ if (val1->getTypeTolerant() != val2->getTypeTolerant()) {
+ return -1;
+ } else {
+ return ScValue::compare(val1, val2);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::setProperty(const char *propName, int value) {
+ ScValue *val = new ScValue(_gameRef, value);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::setProperty(const char *propName, const char *value) {
+ ScValue *val = new ScValue(_gameRef, value);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::setProperty(const char *propName, double value) {
+ ScValue *val = new ScValue(_gameRef, value);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::setProperty(const char *propName, bool value) {
+ ScValue *val = new ScValue(_gameRef, value);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool ScValue::setProperty(const char *propName) {
+ ScValue *val = new ScValue(_gameRef);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/script_value.h b/engines/wintermute/base/scriptables/script_value.h
new file mode 100644
index 0000000000..bf7d9cd8a1
--- /dev/null
+++ b/engines/wintermute/base/scriptables/script_value.h
@@ -0,0 +1,113 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SCVALUE_H
+#define WINTERMUTE_SCVALUE_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/scriptables/dcscript.h" // Added by ClassView
+#include "common/str.h"
+
+namespace Wintermute {
+
+class ScScript;
+class BaseScriptable;
+
+class ScValue : public BaseClass {
+public:
+ static int compare(ScValue *val1, ScValue *val2);
+ static int compareStrict(ScValue *val1, ScValue *val2);
+ TValType getTypeTolerant();
+ void cleanup(bool ignoreNatives = false);
+ DECLARE_PERSISTENT(ScValue, BaseClass)
+
+ bool _isConstVar;
+ bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+ void setValue(ScValue *val);
+ bool _persistent;
+ bool propExists(const char *name);
+ void copy(ScValue *orig, bool copyWhole = false);
+ void setStringVal(const char *val);
+ TValType getType();
+ bool getBool(bool defaultVal = false);
+ int getInt(int defaultVal = 0);
+ double getFloat(double defaultVal = 0.0f);
+ const char *getString();
+ void *getMemBuffer();
+ BaseScriptable *getNative();
+ bool deleteProp(const char *name);
+ void deleteProps();
+ void CleanProps(bool includingNatives);
+ void setBool(bool val);
+ void setInt(int val);
+ void setFloat(double val);
+ void setString(const char *val);
+ void setString(const Common::String &val);
+ void setNULL();
+ void setNative(BaseScriptable *val, bool persistent = false);
+ void setObject();
+ void setReference(ScValue *val);
+ bool isNULL();
+ bool isNative();
+ bool isString();
+ bool isBool();
+ bool isFloat();
+ bool isInt();
+ bool isObject();
+ bool setProp(const char *name, ScValue *val, bool copyWhole = false, bool setAsConst = false);
+ ScValue *getProp(const char *name);
+ BaseScriptable *_valNative;
+ ScValue *_valRef;
+private:
+ bool _valBool;
+ int _valInt;
+ double _valFloat;
+ char *_valString;
+public:
+ TValType _type;
+ ScValue(BaseGame *inGame);
+ ScValue(BaseGame *inGame, bool Val);
+ ScValue(BaseGame *inGame, int Val);
+ ScValue(BaseGame *inGame, double Val);
+ ScValue(BaseGame *inGame, const char *Val);
+ virtual ~ScValue();
+ Common::HashMap<Common::String, ScValue *> _valObject;
+ Common::HashMap<Common::String, ScValue *>::iterator _valIter;
+
+ bool setProperty(const char *propName, int value);
+ bool setProperty(const char *propName, const char *value);
+ bool setProperty(const char *propName, double value);
+ bool setProperty(const char *propName, bool value);
+ bool setProperty(const char *propName);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/sound/base_sound.cpp b/engines/wintermute/base/sound/base_sound.cpp
new file mode 100644
index 0000000000..00d07cd3c2
--- /dev/null
+++ b/engines/wintermute/base/sound/base_sound.cpp
@@ -0,0 +1,292 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/base/sound/base_sound_buffer.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(BaseSound, false)
+
+BaseSound::BaseSound(BaseGame *inGame) : BaseClass(inGame) {
+ _sound = NULL;
+ _soundFilename = "";
+
+ _soundType = Audio::Mixer::kSFXSoundType;
+ _soundStreamed = false;
+ _soundLooping = false;
+ _soundPlaying = false;
+ _soundPaused = false;
+ _soundFreezePaused = false;
+ _soundPosition = 0;
+ _soundPrivateVolume = 0;
+ _soundLoopStart = 0;
+
+ _sFXType = SFX_NONE;
+ _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0;
+}
+
+BaseSound::~BaseSound() {
+ if (_sound) {
+ _gameRef->_soundMgr->removeSound(_sound);
+ }
+ _sound = NULL;
+}
+
+bool BaseSound::setSound(const Common::String &filename, Audio::Mixer::SoundType type, bool streamed) {
+ if (_sound) {
+ _gameRef->_soundMgr->removeSound(_sound);
+ _sound = NULL;
+ }
+ _soundFilename = Common::String(); // Set empty
+
+ _sound = _gameRef->_soundMgr->addSound(filename, type, streamed);
+ if (_sound) {
+ _soundFilename = filename;
+
+ _soundType = type;
+ _soundStreamed = streamed;
+
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+bool BaseSound::setSoundSimple() {
+ _sound = _gameRef->_soundMgr->addSound(_soundFilename, _soundType, _soundStreamed);
+ if (_sound) {
+ if (_soundPosition) {
+ _sound->setPosition(_soundPosition);
+ }
+ _sound->setLooping(_soundLooping);
+ _sound->setPrivateVolume(_soundPrivateVolume);
+ _sound->setLoopStart(_soundLoopStart);
+ _sound->_freezePaused = _soundFreezePaused;
+ if (_soundPlaying) {
+ return _sound->resume();
+ } else {
+ return STATUS_OK;
+ }
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+uint32 BaseSound::getLength() {
+ if (_sound) {
+ return _sound->getLength();
+ } else {
+ return 0;
+ }
+}
+
+bool BaseSound::play(bool looping) {
+ if (_sound) {
+ _soundPaused = false;
+ return _sound->play(looping, _soundPosition);
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+bool BaseSound::stop() {
+ if (_sound) {
+ _soundPaused = false;
+ return _sound->stop();
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+bool BaseSound::pause(bool freezePaused) {
+ if (_sound) {
+ _soundPaused = true;
+ if (freezePaused) {
+ _sound->_freezePaused = true;
+ }
+ return _sound->pause();
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+bool BaseSound::resume() {
+ if (_sound && _soundPaused) {
+ _soundPaused = false;
+ return _sound->resume();
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+bool BaseSound::persist(BasePersistenceManager *persistMgr) {
+ if (persistMgr->getIsSaving() && _sound) {
+ _soundPlaying = _sound->isPlaying();
+ _soundLooping = _sound->_looping;
+ _soundPrivateVolume = _sound->_privateVolume;
+ if (_soundPlaying) {
+ _soundPosition = _sound->getPosition();
+ }
+ _soundLoopStart = _sound->_loopStart;
+ _soundFreezePaused = _sound->_freezePaused;
+ }
+
+ if (persistMgr->getIsSaving()) {
+ _sFXType = SFX_NONE;
+ _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0;
+ }
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER(_soundFilename));
+ persistMgr->transfer(TMEMBER(_soundLooping));
+ persistMgr->transfer(TMEMBER(_soundPaused));
+ persistMgr->transfer(TMEMBER(_soundFreezePaused));
+ persistMgr->transfer(TMEMBER(_soundPlaying));
+ persistMgr->transfer(TMEMBER(_soundPosition));
+ persistMgr->transfer(TMEMBER(_soundPrivateVolume));
+ persistMgr->transfer(TMEMBER(_soundStreamed));
+ persistMgr->transfer(TMEMBER_INT(_soundType));
+ persistMgr->transfer(TMEMBER(_soundLoopStart));
+
+ return STATUS_OK;
+}
+
+bool BaseSound::isPlaying() {
+ return _sound && _sound->isPlaying();
+}
+
+bool BaseSound::isPaused() {
+ return _sound && _soundPaused;
+}
+
+bool BaseSound::setPositionTime(uint32 time) {
+ if (!_sound) {
+ return STATUS_FAILED;
+ }
+ _soundPosition = time;
+ bool ret = _sound->setPosition(_soundPosition);
+ if (_sound->isPlaying()) {
+ _soundPosition = 0;
+ }
+ return ret;
+}
+
+uint32 BaseSound::getPositionTime() {
+ if (!_sound) {
+ return 0;
+ }
+
+ if (!_sound->isPlaying()) {
+ return 0;
+ } else {
+ return _sound->getPosition();
+ }
+}
+
+bool BaseSound::setVolumePercent(int percent) {
+ if (!_sound) {
+ return STATUS_FAILED;
+ } else {
+ return _sound->setPrivateVolume(percent * 255 / 100);
+ }
+}
+
+bool BaseSound::setVolume(int volume) {
+ if (!_sound) {
+ return STATUS_FAILED;
+ } else {
+ return _sound->setPrivateVolume(volume);
+ }
+}
+
+bool BaseSound::setPrivateVolume(int volume) {
+ if (!_sound) {
+ return STATUS_FAILED;
+ } else {
+ _sound->_privateVolume = volume;
+ return STATUS_OK;
+ }
+}
+
+int BaseSound::getVolumePercent() {
+ if (!_sound) {
+ return 0;
+ } else {
+ return _sound->_privateVolume * 100 / 255;
+ }
+}
+
+int BaseSound::getVolume() {
+ if (!_sound) {
+ return 0;
+ } else {
+ return _sound->_privateVolume;
+ }
+}
+
+bool BaseSound::setLoopStart(uint32 pos) {
+ if (!_sound) {
+ return STATUS_FAILED;
+ } else {
+ _sound->setLoopStart(pos);
+ return STATUS_OK;
+ }
+}
+
+bool BaseSound::setPan(float pan) {
+ if (_sound) {
+ return _sound->setPan(pan);
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+bool BaseSound::applyFX(TSFXType type, float param1, float param2, float param3, float param4) {
+ if (!_sound) {
+ return STATUS_OK;
+ }
+
+ if (type != _sFXType || param1 != _sFXParam1 || param2 != _sFXParam2 || param3 != _sFXParam3 || param4 != _sFXParam4) {
+ bool ret = _sound->applyFX(type, param1, param2, param3, param4);
+
+ _sFXType = type;
+ _sFXParam1 = param1;
+ _sFXParam2 = param2;
+ _sFXParam3 = param3;
+ _sFXParam4 = param4;
+
+ return ret;
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/sound/base_sound.h b/engines/wintermute/base/sound/base_sound.h
new file mode 100644
index 0000000000..637061b7cc
--- /dev/null
+++ b/engines/wintermute/base/sound/base_sound.h
@@ -0,0 +1,87 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SOUND_H
+#define WINTERMUTE_BASE_SOUND_H
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/dctypes.h" // Added by ClassView
+#include "engines/wintermute/persistent.h"
+#include "audio/mixer.h"
+
+namespace Wintermute {
+
+class BaseSoundBuffer;
+class BaseSound : public BaseClass {
+public:
+ bool setPan(float pan);
+ int getVolume();
+ int getVolumePercent();
+ bool setVolumePercent(int percent);
+ bool setVolume(int volume);
+ bool setPrivateVolume(int volume);
+ bool setLoopStart(uint32 pos);
+ uint32 getPositionTime();
+ bool setPositionTime(uint32 time);
+ bool isPlaying();
+ bool isPaused();
+ DECLARE_PERSISTENT(BaseSound, BaseClass)
+ bool resume();
+ bool pause(bool freezePaused = false);
+ bool stop();
+ bool play(bool looping = false);
+ uint32 getLength();
+ const char *getFilename() { return _soundFilename.c_str(); }
+ bool setSoundSimple();
+ bool setSound(const Common::String &filename, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType, bool streamed = false);
+ BaseSound(BaseGame *inGame);
+ virtual ~BaseSound();
+
+ bool applyFX(TSFXType type = SFX_NONE, float param1 = 0, float param2 = 0, float param3 = 0, float param4 = 0);
+private:
+ Common::String _soundFilename;
+ bool _soundStreamed;
+ Audio::Mixer::SoundType _soundType;
+ int _soundPrivateVolume;
+ uint32 _soundLoopStart;
+ uint32 _soundPosition;
+ bool _soundPlaying;
+ bool _soundLooping;
+ bool _soundPaused;
+ bool _soundFreezePaused;
+ TSFXType _sFXType;
+ float _sFXParam1;
+ float _sFXParam2;
+ float _sFXParam3;
+ float _sFXParam4;
+ BaseSoundBuffer *_sound;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/sound/base_sound_buffer.cpp b/engines/wintermute/base/sound/base_sound_buffer.cpp
new file mode 100644
index 0000000000..250570f2b8
--- /dev/null
+++ b/engines/wintermute/base/sound/base_sound_buffer.cpp
@@ -0,0 +1,295 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/base/sound/base_sound_buffer.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/wintermute.h"
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "audio/decoders/vorbis.h"
+#include "audio/decoders/wave.h"
+#include "audio/decoders/raw.h"
+#include "common/system.h"
+#include "common/substream.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+#define MAX_NONSTREAMED_FILE_SIZE 1024*1024
+
+//////////////////////////////////////////////////////////////////////////
+BaseSoundBuffer::BaseSoundBuffer(BaseGame *inGame) : BaseClass(inGame) {
+ _stream = NULL;
+ _handle = NULL;
+// _sync = NULL;
+
+ _streamed = false;
+ _filename = "";
+ _file = NULL;
+ _privateVolume = 255;
+ _volume = 255;
+
+ _looping = false;
+ _loopStart = 0;
+ _startPos = 0;
+
+ _type = Audio::Mixer::kSFXSoundType;
+
+ _freezePaused = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSoundBuffer::~BaseSoundBuffer() {
+ stop();
+
+ if (_handle) {
+ g_system->getMixer()->stopHandle(*_handle);
+ delete _handle;
+ _handle = NULL;
+ }
+ delete _stream;
+ _stream = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSoundBuffer::setStreaming(bool streamed, uint32 numBlocks, uint32 blockSize) {
+ _streamed = streamed;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::loadFromFile(const Common::String &filename, bool forceReload) {
+ debugC(kWintermuteDebugAudio, "BSoundBuffer::LoadFromFile(%s,%d)", filename.c_str(), forceReload);
+
+ // Load a file, but avoid having the File-manager handle the disposal of it.
+ _file = BaseFileManager::getEngineInstance()->openFile(filename, true, false);
+ if (!_file) {
+ _gameRef->LOG(0, "Error opening sound file '%s'", filename.c_str());
+ return STATUS_FAILED;
+ }
+ Common::String strFilename(filename);
+ strFilename.toLowercase();
+ if (strFilename.hasSuffix(".ogg")) {
+ _stream = Audio::makeVorbisStream(_file, DisposeAfterUse::YES);
+ } else if (strFilename.hasSuffix(".wav")) {
+ int waveSize, waveRate;
+ byte waveFlags;
+ uint16 waveType;
+
+ if (Audio::loadWAVFromStream(*_file, waveSize, waveRate, waveFlags, &waveType)) {
+ if (waveType == 1) {
+ // We need to wrap the file in a substream to make sure the size is right.
+ _file = new Common::SeekableSubReadStream(_file, 0, waveSize);
+ _stream = Audio::makeRawStream(_file, waveRate, waveFlags, DisposeAfterUse::YES);
+ } else {
+ error("BSoundBuffer::LoadFromFile - WAVE not supported yet for %s with type %d", filename.c_str(), waveType);
+ }
+ }
+ } else {
+ error("BSoundBuffer::LoadFromFile - Unknown filetype for %s", filename.c_str());
+ }
+ if (!_stream) {
+ return STATUS_FAILED;
+ }
+ _filename = filename;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::play(bool looping, uint32 startSample) {
+ if (_handle) {
+ g_system->getMixer()->stopHandle(*_handle);
+ delete _handle;
+ _handle = NULL;
+ }
+ // Store the loop-value for save-games.
+ setLooping(looping);
+ if (_stream) {
+ _stream->seek(startSample);
+ _handle = new Audio::SoundHandle;
+ if (_looping) {
+ Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO);
+ g_system->getMixer()->playStream(_type, _handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES);
+ } else {
+ g_system->getMixer()->playStream(_type, _handle, _stream, -1, _volume, 0, DisposeAfterUse::NO);
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSoundBuffer::setLooping(bool looping) {
+ if (isPlaying()) {
+ // This warning is here, to see if this is ever the case.
+ warning("BSoundBuffer::SetLooping(%d) - won't change a playing sound", looping); // TODO
+ }
+ _looping = looping;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::resume() {
+ // If the sound was paused while active:
+ if (_stream && _handle) {
+ g_system->getMixer()->pauseHandle(*_handle, false);
+ } else if (_stream) { // Otherwise we come from a savegame, and thus have no handle
+ play(_looping, _startPos);
+ } else {
+ warning("BaseSoundBuffer::resume - Called without a handle or a stream");
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::stop() {
+ if (_stream && _handle) {
+ g_system->getMixer()->stopHandle(*_handle);
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::pause() {
+ if (_stream && _handle) {
+ g_system->getMixer()->pauseHandle(*_handle, true);
+ }
+ return STATUS_OK;
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseSoundBuffer::getLength() {
+ if (_stream) {
+ uint32 len = _stream->getLength().msecs();
+ return len * 1000;
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSoundBuffer::setType(Audio::Mixer::SoundType type) {
+ _type = type;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSoundBuffer::updateVolume() {
+ setVolume(_privateVolume);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::setVolume(int volume) {
+ _volume = volume * _gameRef->_soundMgr->getMasterVolume() / 255;
+ if (_stream && _handle) {
+ byte vol = (byte)(_volume);
+ g_system->getMixer()->setChannelVolume(*_handle, vol);
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::setPrivateVolume(int volume) {
+ _privateVolume = volume;
+ return setVolume(_privateVolume);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::isPlaying() {
+ if (_stream && _handle) {
+ return _freezePaused || g_system->getMixer()->isSoundHandleActive(*_handle);
+ } else {
+ return false;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseSoundBuffer::getPosition() {
+ if (_stream && _handle) {
+ uint32 pos = g_system->getMixer()->getSoundElapsedTime(*_handle);
+ return pos;
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::setPosition(uint32 pos) {
+ if (isPlaying()) {
+ warning("BaseSoundBuffer::SetPosition - not implemented for playing sounds yet.");
+ }
+ _startPos = pos;
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::setLoopStart(uint32 pos) {
+ _loopStart = pos;
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::setPan(float pan) {
+ if (_handle) {
+ g_system->getMixer()->setChannelBalance(*_handle, (int8)(pan * 127));
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundBuffer::applyFX(TSFXType type, float param1, float param2, float param3, float param4) {
+ // This function was already stubbed out in WME Lite, and thus isn't reimplemented here either.
+ switch (type) {
+ case SFX_ECHO:
+ //warning("BaseSoundBuffer::ApplyFX(SFX_ECHO, %f, %f, %f, %f) - not implemented yet", param1, param2, param3, param4);
+ break;
+
+ case SFX_REVERB:
+ //warning("BaseSoundBuffer::ApplyFX(SFX_REVERB, %f, %f, %f, %f) - not implemented yet", param1, param2, param3, param4);
+ break;
+
+ default:
+ break;
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/sound/base_sound_buffer.h b/engines/wintermute/base/sound/base_sound_buffer.h
new file mode 100644
index 0000000000..9c39f4c34b
--- /dev/null
+++ b/engines/wintermute/base/sound/base_sound_buffer.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SOUNDBUFFER_H
+#define WINTERMUTE_BASE_SOUNDBUFFER_H
+
+
+#include "engines/wintermute/base/base.h"
+#include "audio/mixer.h"
+#include "common/stream.h"
+
+namespace Audio {
+class SeekableAudioStream;
+class SoundHandle;
+}
+
+namespace Wintermute {
+
+class BaseFile;
+class BaseSoundBuffer : public BaseClass {
+public:
+
+ BaseSoundBuffer(BaseGame *inGame);
+ virtual ~BaseSoundBuffer();
+
+ bool pause();
+ bool play(bool looping = false, uint32 startSample = 0);
+ bool resume();
+ bool stop();
+ bool isPlaying();
+
+ void setLooping(bool looping);
+
+ uint32 getPosition();
+ bool setPosition(uint32 pos);
+ uint32 getLength();
+
+ bool setLoopStart(uint32 pos);
+ uint32 getLoopStart() const {
+ return _loopStart;
+ }
+
+ bool setPan(float pan);
+ bool setPrivateVolume(int colume);
+ bool setVolume(int colume);
+ void updateVolume();
+
+ void setType(Audio::Mixer::SoundType Type);
+
+ bool loadFromFile(const Common::String &filename, bool forceReload = false);
+ void setStreaming(bool streamed, uint32 numBlocks = 0, uint32 blockSize = 0);
+ bool applyFX(TSFXType type, float param1, float param2, float param3, float param4);
+
+ //HSTREAM _stream;
+ //HSYNC _sync;
+ Audio::SeekableAudioStream *_stream;
+ Audio::SoundHandle *_handle;
+
+ bool _freezePaused;
+ uint32 _loopStart;
+ Audio::Mixer::SoundType _type;
+ bool _looping;
+
+ int _privateVolume;
+private:
+ uint32 _startPos;
+ Common::String _filename;
+ bool _streamed;
+ Common::SeekableReadStream *_file;
+ int _volume;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/sound/base_sound_manager.cpp b/engines/wintermute/base/sound/base_sound_manager.cpp
new file mode 100644
index 0000000000..441793144d
--- /dev/null
+++ b/engines/wintermute/base/sound/base_sound_manager.cpp
@@ -0,0 +1,283 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/utils/path_util.h"
+#include "engines/wintermute/utils/string_util.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/sound/base_sound_buffer.h"
+#include "engines/wintermute/wintermute.h"
+#include "common/config-manager.h"
+#include "audio/mixer.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+//IMPLEMENT_PERSISTENT(BaseSoundMgr, true);
+
+//////////////////////////////////////////////////////////////////////////
+BaseSoundMgr::BaseSoundMgr(BaseGame *inGame) : BaseClass(inGame) {
+ _soundAvailable = false;
+ _volumeMaster = 255;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+BaseSoundMgr::~BaseSoundMgr() {
+ saveSettings();
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::cleanup() {
+ for (uint32 i = 0; i < _sounds.size(); i++) {
+ delete _sounds[i];
+ }
+ _sounds.clear();
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseSoundMgr::saveSettings() {
+ if (_soundAvailable) {
+ ConfMan.setInt("master_volume", _volumeMaster);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::initialize() {
+ _soundAvailable = false;
+
+ if (!g_system->getMixer()->isReady()) {
+ return STATUS_FAILED;
+ }
+ _volumeMaster = (ConfMan.hasKey("master_volume") ? ConfMan.getInt("master_volume") : 255);
+ _soundAvailable = true;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseSoundBuffer *BaseSoundMgr::addSound(const Common::String &filename, Audio::Mixer::SoundType type, bool streamed) {
+ if (!_soundAvailable) {
+ return NULL;
+ }
+
+ BaseSoundBuffer *sound;
+
+ Common::String useFilename = filename;
+ // try to switch WAV to OGG file (if available)
+ AnsiString ext = PathUtil::getExtension(filename);
+ if (StringUtil::compareNoCase(ext, "wav")) {
+ AnsiString path = PathUtil::getDirectoryName(filename);
+ AnsiString name = PathUtil::getFileNameWithoutExtension(filename);
+
+ AnsiString newFile = PathUtil::combine(path, name + "ogg");
+ if (BaseFileManager::getEngineInstance()->hasFile(newFile)) {
+ useFilename = newFile;
+ }
+ }
+
+ sound = new BaseSoundBuffer(_gameRef);
+ if (!sound) {
+ return NULL;
+ }
+
+ sound->setStreaming(streamed);
+ sound->setType(type);
+
+
+ bool res = sound->loadFromFile(useFilename);
+ if (DID_FAIL(res)) {
+ _gameRef->LOG(res, "Error loading sound '%s'", useFilename.c_str());
+ delete sound;
+ return NULL;
+ }
+
+ // Make sure the master-volume is applied to the sound.
+ sound->updateVolume();
+
+ // register sound
+ _sounds.push_back(sound);
+
+ return sound;
+
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::addSound(BaseSoundBuffer *sound, Audio::Mixer::SoundType type) {
+ if (!sound) {
+ return STATUS_FAILED;
+ }
+
+ // Make sure the master-volume is applied to the sound.
+ sound->updateVolume();
+
+ // register sound
+ _sounds.push_back(sound);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::removeSound(BaseSoundBuffer *sound) {
+ for (uint32 i = 0; i < _sounds.size(); i++) {
+ if (_sounds[i] == sound) {
+ delete _sounds[i];
+ _sounds.remove_at(i);
+ return STATUS_OK;
+ }
+ }
+
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::setVolume(Audio::Mixer::SoundType type, int volume) {
+ if (!_soundAvailable) {
+ return STATUS_OK;
+ }
+
+ switch (type) {
+ case Audio::Mixer::kSFXSoundType:
+ ConfMan.setInt("sfx_volume", volume);
+ break;
+ case Audio::Mixer::kSpeechSoundType:
+ ConfMan.setInt("speech_volume", volume);
+ break;
+ case Audio::Mixer::kMusicSoundType:
+ ConfMan.setInt("music_volume", volume);
+ break;
+ case Audio::Mixer::kPlainSoundType:
+ error("Plain sound type shouldn't be used in WME");
+ }
+ g_engine->syncSoundSettings();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::setVolumePercent(Audio::Mixer::SoundType type, byte percent) {
+ return setVolume(type, percent * 255 / 100);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte BaseSoundMgr::getVolumePercent(Audio::Mixer::SoundType type) {
+ int volume = 0;
+
+ switch (type) {
+ case Audio::Mixer::kSFXSoundType:
+ case Audio::Mixer::kSpeechSoundType:
+ case Audio::Mixer::kMusicSoundType:
+ volume = g_system->getMixer()->getVolumeForSoundType(type);
+ break;
+ default:
+ error("Sound-type not set");
+ break;
+ }
+
+ return (byte)(volume * 100 / 255);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::setMasterVolume(byte value) {
+ _volumeMaster = value;
+ for (uint32 i = 0; i < _sounds.size(); i++) {
+ _sounds[i]->updateVolume();
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::setMasterVolumePercent(byte percent) {
+ setMasterVolume(percent * 255 / 100);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte BaseSoundMgr::getMasterVolumePercent() {
+ return getMasterVolume() * 100 / 255;
+}
+
+//////////////////////////////////////////////////////////////////////////
+byte BaseSoundMgr::getMasterVolume() {
+ return (byte)_volumeMaster;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::pauseAll(bool includingMusic) {
+
+ for (uint32 i = 0; i < _sounds.size(); i++) {
+ if (_sounds[i]->isPlaying() && (_sounds[i]->_type != Audio::Mixer::kMusicSoundType || includingMusic)) {
+ _sounds[i]->pause();
+ _sounds[i]->_freezePaused = true;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSoundMgr::resumeAll() {
+
+ for (uint32 i = 0; i < _sounds.size(); i++) {
+ if (_sounds[i]->_freezePaused) {
+ _sounds[i]->resume();
+ _sounds[i]->_freezePaused = false;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float BaseSoundMgr::posToPan(int x, int y) {
+ float relPos = (float)x / ((float)_gameRef->_renderer->_width);
+
+ float minPan = -0.7f;
+ float maxPan = 0.7f;
+
+ return minPan + relPos * (maxPan - minPan);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/base/sound/base_sound_manager.h b/engines/wintermute/base/sound/base_sound_manager.h
new file mode 100644
index 0000000000..36a729b5ae
--- /dev/null
+++ b/engines/wintermute/base/sound/base_sound_manager.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_BASE_SOUNDMGR_H
+#define WINTERMUTE_BASE_SOUNDMGR_H
+
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/base/base.h"
+#include "audio/mixer.h"
+#include "common/array.h"
+
+namespace Wintermute {
+class BaseSoundBuffer;
+class BaseSoundMgr : public BaseClass {
+public:
+ float posToPan(int x, int y);
+ bool resumeAll();
+ bool pauseAll(bool includingMusic = true);
+ bool cleanup();
+ //DECLARE_PERSISTENT(BaseSoundMgr, BaseClass);
+ byte getMasterVolumePercent();
+ byte getMasterVolume();
+ bool setMasterVolume(byte percent);
+ bool setMasterVolumePercent(byte percent);
+ byte getVolumePercent(Audio::Mixer::SoundType type);
+ bool setVolumePercent(Audio::Mixer::SoundType type, byte percent);
+ bool setVolume(Audio::Mixer::SoundType type, int volume);
+ uint32 _volumeOriginal;
+ int _volumeMaster;
+ bool removeSound(BaseSoundBuffer *sound);
+ BaseSoundBuffer *addSound(const Common::String &filename, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType, bool streamed = false);
+ bool addSound(BaseSoundBuffer *sound, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType);
+ bool initialize();
+ bool _soundAvailable;
+ BaseSoundMgr(BaseGame *inGame);
+ virtual ~BaseSoundMgr();
+ Common::Array<BaseSoundBuffer *> _sounds;
+ void saveSettings();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/coll_templ.h b/engines/wintermute/coll_templ.h
new file mode 100644
index 0000000000..493ea07015
--- /dev/null
+++ b/engines/wintermute/coll_templ.h
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_COLL_TEMPL_H
+#define WINTERMUTE_COLL_TEMPL_H
+
+#include "common/array.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+namespace Wintermute {
+
+// Basically Common::Array with peristence-support.
+template<typename TYPE>
+class BaseArray : public Common::Array<TYPE> {
+public:
+// TODO: Might want to make sure that destructors are called when replacing/deleting/getting destructed
+ bool persist(BasePersistenceManager *persistMgr) {
+ int j;
+ if (persistMgr->getIsSaving()) {
+ j = Common::Array<TYPE>::size();
+ persistMgr->transfer("ArraySize", &j);
+ typename Common::Array<TYPE>::const_iterator it = Common::Array<TYPE>::begin();
+ for (; it != Common::Array<TYPE>::end(); ++it) {
+ TYPE obj = *it;
+ persistMgr->transfer("", &obj);
+ }
+ } else {
+ Common::Array<TYPE>::clear();
+ persistMgr->transfer("ArraySize", &j);
+ for (int i = 0; i < j; i++) {
+ TYPE obj;
+ persistMgr->transfer("", &obj);
+ add(obj);
+ }
+ }
+ return true;
+ }
+ int add(TYPE newElement) {
+ Common::Array<TYPE>::push_back(newElement);
+ return Common::Array<TYPE>::size() - 1;
+ }
+ void remove_at(uint32 idx) {
+ Common::Array<TYPE>::remove_at(idx);
+ }
+ void remove_at(uint32 idx, uint32 num) {
+ while (num) {
+ if (idx >= Common::Array<TYPE>::size()) {
+ break;
+ }
+ Common::Array<TYPE>::remove_at(idx);
+ }
+ }
+ template<typename T2>
+ void copy(const BaseArray<T2> &src) {
+ Common::Array<TYPE>::insert_at(0, src);
+ }
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/dcgf.h b/engines/wintermute/dcgf.h
new file mode 100644
index 0000000000..fc4174094b
--- /dev/null
+++ b/engines/wintermute/dcgf.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_DCGF_H
+#define WINTERMUTE_DCGF_H
+
+
+//////////////////////////////////////////////////////////////////////////
+#define DCGF_VER_MAJOR 1
+#define DCGF_VER_MINOR 1
+#define DCGF_VER_BUILD 1
+#define DCGF_VER_SUFFIX "beta"
+#define DCGF_VER_BETA true
+
+#define DCGF_NAME "WME Lite"
+#define DCGF_MAGIC 0xDEC0ADDE
+
+// minimal saved game version we support
+#define SAVEGAME_VER_MAJOR 1
+#define SAVEGAME_VER_MINOR 1
+#define SAVEGAME_VER_BUILD 1
+//////////////////////////////////////////////////////////////////////////
+
+#define COMPRESSED_FILE_MAGIC 0x504D435A // ZCMP
+
+#endif
diff --git a/engines/wintermute/dctypes.h b/engines/wintermute/dctypes.h
new file mode 100644
index 0000000000..bd4966eb6b
--- /dev/null
+++ b/engines/wintermute/dctypes.h
@@ -0,0 +1,225 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_DCTYPES_H
+#define WINTERMUTE_DCTYPES_H
+
+
+#include "common/str.h"
+#include "common/list.h"
+#include "common/array.h"
+
+namespace Wintermute {
+
+//typedef std::string AnsiString;
+//typedef std::string Utf8String;
+//typedef std::wstring WideString;
+typedef Common::String AnsiString;
+typedef Common::String Utf8String;
+typedef Common::String WideString; // NB: Not actually true I presume.
+
+typedef Common::List<WideString> WideStringList;
+typedef Common::List<AnsiString> AnsiStringList;
+
+typedef Common::Array<WideString> WideStringArray;
+typedef Common::Array<AnsiString> AnsiStringArray;
+
+
+enum TGameState {
+ GAME_RUNNING,
+ GAME_FROZEN,
+ GAME_SEMI_FROZEN
+};
+
+
+enum TImageType {
+ IMG_PALETTED8,
+ IMG_TRUECOLOR
+};
+
+
+enum TTextAlign {
+ TAL_LEFT = 0,
+ TAL_RIGHT,
+ TAL_CENTER,
+ NUM_TEXT_ALIGN
+};
+
+
+enum TVerticalAlign {
+ VAL_TOP = 0,
+ VAL_CENTER,
+ VAL_BOTTOM,
+ NUM_VERTICAL_ALIGN
+};
+
+
+enum TDirection {
+ DI_UP = 0,
+ DI_UPRIGHT = 1,
+ DI_RIGHT = 2,
+ DI_DOWNRIGHT = 3,
+ DI_DOWN = 4,
+ DI_DOWNLEFT = 5,
+ DI_LEFT = 6,
+ DI_UPLEFT = 7,
+ NUM_DIRECTIONS = 8,
+ DI_NONE = 9
+};
+
+enum TEventType {
+ EVENT_NONE = 0,
+ EVENT_INIT = 1,
+ EVENT_SHUTDOWN = 2,
+ EVENT_LEFT_CLICK = 3,
+ EVENT_RIGHT_CLICK = 4,
+ EVENT_MIDDLE_CLICK = 5,
+ EVENT_LEFT_DBLCLICK = 6,
+ EVENT_PRESS = 7,
+ EVENT_IDLE = 8,
+ EVENT_MOUSE_OVER = 9,
+ EVENT_LEFT_RELEASE = 10,
+ EVENT_RIGHT_RELEASE = 11,
+ EVENT_MIDDLE_RELEASE = 12,
+ NUM_EVENTS
+};
+
+enum TUIObjectType {
+ UI_UNKNOWN,
+ UI_BUTTON,
+ UI_WINDOW,
+ UI_STATIC,
+ UI_EDIT,
+ UI_HTML,
+ UI_CUSTOM
+};
+
+
+enum TRendererState {
+ RSTATE_3D,
+ RSTATE_2D,
+ RSTATE_LINES,
+ RSTATE_NONE
+};
+
+
+enum TDynamicConstructor {
+ DYNAMIC_CONSTRUCTOR
+};
+
+
+enum TVideoMode {
+ VIDEO_WINDOW,
+ VIDEO_FULLSCREEN,
+ VIDEO_ANY
+};
+
+
+enum TVideoPlayback {
+ VID_PLAY_POS = 0,
+ VID_PLAY_STRETCH = 1,
+ VID_PLAY_CENTER = 2
+};
+
+
+enum TMouseEvent {
+ MOUSE_CLICK,
+ MOUSE_RELEASE,
+ MOUSE_DBLCLICK
+};
+
+
+enum TMouseButton {
+ MOUSE_BUTTON_LEFT,
+ MOUSE_BUTTON_RIGHT,
+ MOUSE_BUTTON_MIDDLE
+};
+
+
+enum TTransMgrState {
+ TRANS_MGR_RUNNING,
+ TRANS_MGR_READY
+};
+
+
+enum TTransitionType {
+ TRANSITION_NONE = 0,
+ TRANSITION_FADE_OUT = 1,
+ TRANSITION_FADE_IN = 2,
+ NUM_TRANSITION_TYPES
+};
+
+
+enum TWindowMode {
+ WINDOW_NORMAL,
+ WINDOW_EXCLUSIVE,
+ WINDOW_SYSTEM_EXCLUSIVE
+};
+
+enum TSFXType {
+ SFX_NONE,
+ SFX_ECHO,
+ SFX_REVERB
+};
+
+
+enum TSpriteCacheType {
+ CACHE_ALL,
+ CACHE_HALF
+};
+
+enum TTextEncoding {
+ TEXT_ANSI = 0,
+ TEXT_UTF8 = 1,
+ NUM_TEXT_ENCODINGS
+};
+
+enum TSpriteBlendMode {
+ BLEND_UNKNOWN = -1,
+ BLEND_NORMAL = 0,
+ BLEND_ADDITIVE = 1,
+ BLEND_SUBTRACTIVE = 2,
+ NUM_BLEND_MODES
+};
+
+enum TTTSType {
+ TTS_CAPTION = 0,
+ TTS_TALK,
+ TTS_KEYPRESS
+};
+
+enum TShadowType {
+ SHADOW_NONE = 0,
+ SHADOW_SIMPLE = 1,
+ SHADOW_FLAT = 2,
+ SHADOW_STENCIL = 3
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp
new file mode 100644
index 0000000000..04f7f3b112
--- /dev/null
+++ b/engines/wintermute/detection.cpp
@@ -0,0 +1,174 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/advancedDetector.h"
+#include "engines/wintermute/wintermute.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+#include "common/config-manager.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/util.h"
+
+#include "engines/metaengine.h"
+
+#include "engines/wintermute/detection_tables.h"
+
+namespace Wintermute {
+
+/**
+ * The fallback game descriptor used by the Wintermute engine's fallbackDetector.
+ * Contents of this struct are overwritten by the fallbackDetector. (logic copied partially
+ * from the SCI-engine).
+ */
+static ADGameDescription s_fallbackDesc = {
+ "",
+ "",
+ AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+};
+static char s_fallbackGameIdBuf[256];
+
+static const char *directoryGlobs[] = {
+ "language", // To detect the various languages
+ 0
+};
+
+class WintermuteMetaEngine : public AdvancedMetaEngine {
+public:
+ WintermuteMetaEngine() : AdvancedMetaEngine(Wintermute::gameDescriptions, sizeof(ADGameDescription), Wintermute::wintermuteGames) {
+ _singleid = "wintermute";
+ _maxScanDepth = 2;
+ _directoryGlobs = directoryGlobs;
+ }
+ virtual const char *getName() const {
+ return "Wintermute";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Copyright (c) 2011 Jan Nedoma";
+ }
+
+ virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+ // Set some defaults
+ s_fallbackDesc.extra = "";
+ s_fallbackDesc.language = Common::UNK_LANG;
+ s_fallbackDesc.flags = ADGF_UNSTABLE;
+ s_fallbackDesc.platform = Common::kPlatformWindows; // default to Windows
+ s_fallbackDesc.gameid = "wintermute";
+ s_fallbackDesc.guioptions = GUIO0();
+
+ if (allFiles.contains("data.dcp")) {
+ Common::String name, caption;
+ if (WintermuteEngine::getGameInfo(fslist, name, caption)) {
+ for (uint32 i = 0; i < name.size(); i++) {
+ // Replace spaces (and other non-alphanumerics) with underscores
+ if (!Common::isAlnum(name[(int32)i])) {
+ name.setChar('_', (uint32)i);
+ }
+ }
+ // Prefix to avoid collisions with actually known games
+ name = "wmefan-" + name;
+ Common::strlcpy(s_fallbackGameIdBuf, name.c_str(), sizeof(s_fallbackGameIdBuf) - 1);
+ s_fallbackDesc.gameid = s_fallbackGameIdBuf;
+ if (caption != name) {
+ caption += " (fangame) ";
+ char *offset = s_fallbackGameIdBuf + name.size() + 1;
+ uint32 remainingLength = (sizeof(s_fallbackGameIdBuf) - 1) - (name.size() + 1);
+ Common::strlcpy(offset, caption.c_str(), remainingLength);
+ s_fallbackDesc.extra = offset;
+ s_fallbackDesc.flags |= ADGF_USEEXTRAASTITLE;
+ }
+ return &s_fallbackDesc;
+ } // Fall through to return 0;
+ }
+ return 0;
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ assert(syst);
+ assert(engine);
+
+ *engine = new Wintermute::WintermuteEngine(syst, desc);
+ return true;
+ }
+
+ bool hasFeature(MetaEngineFeature f) const {
+ switch (f) {
+ case MetaEngine::kSupportsListSaves:
+ return true;
+ case MetaEngine::kSupportsLoadingDuringStartup:
+ return true;
+ case MetaEngine::kSupportsDeleteSave:
+ return true;
+ case MetaEngine::kSavesSupportCreationDate:
+ return true;
+ case MetaEngine::kSavesSupportMetaInfo:
+ return true;
+ case MetaEngine::kSavesSupportThumbnail:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ SaveStateList listSaves(const char *target) const {
+ SaveStateList saves;
+ Wintermute::BasePersistenceManager pm(target, true);
+ for (int i = 0; i < getMaximumSaveSlot(); i++) {
+ if (pm.getSaveExists(i)) {
+ SaveStateDescriptor desc;
+ pm.getSaveStateDesc(i, desc);
+ saves.push_back(desc);
+ }
+ }
+ return saves;
+ }
+
+ int getMaximumSaveSlot() const {
+ return 100;
+ }
+
+ void removeSaveState(const char *target, int slot) const {
+ Wintermute::BasePersistenceManager pm(target, true);
+ pm.deleteSaveSlot(slot);
+ }
+
+ virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const {
+ Wintermute::BasePersistenceManager pm(target, true);
+ SaveStateDescriptor retVal;
+ retVal.setDescription("Invalid savegame");
+ pm.getSaveStateDesc(slot, retVal);
+ return retVal;
+ }
+};
+
+} // end of namespace Wintermute
+
+#if PLUGIN_ENABLED_DYNAMIC(WINTERMUTE)
+REGISTER_PLUGIN_DYNAMIC(WINTERMUTE, PLUGIN_TYPE_ENGINE, Wintermute::WintermuteMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(WINTERMUTE, PLUGIN_TYPE_ENGINE, Wintermute::WintermuteMetaEngine);
+#endif
diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h
new file mode 100644
index 0000000000..4f8a962591
--- /dev/null
+++ b/engines/wintermute/detection_tables.h
@@ -0,0 +1,265 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+namespace Wintermute {
+
+static const PlainGameDescriptor wintermuteGames[] = {
+ {"5ld", "Five Lethal Demons"},
+ {"5ma", "Five Magical Amulets"},
+ {"actualdest", "Actual Destination"},
+ {"chivalry", "Chivalry is Not Dead"},
+ {"deadcity", "Dead City"},
+ {"dirtysplit", "Dirty Split"},
+ {"eastside", "East Side Story"},
+ {"ghostsheet", "Ghost in the Sheet"},
+ {"hamlet", "Hamlet or the last game without MMORPS features, shaders and product placement"},
+ {"julia", "J.U.L.I.A."},
+ {"mirage", "Mirage"},
+ {"pigeons", "Pigeons in the Park"},
+ {"reversion", "Reversion"},
+ {"rosemary", "Rosemary"},
+ {"thebox", "The Box"},
+ {"twc", "the white chamber"},
+ {"wintermute", "Wintermute engine game"},
+ {0, 0}
+};
+
+static const ADGameDescription gameDescriptions[] = {
+ // Five Lethal Demons
+ {
+ "5ld",
+ "",
+ AD_ENTRY1s("data.dcp", "1037a77cbd001e0644898addc022322c", 15407750),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Five Magical Amulets
+ {
+ "5ma",
+ "",
+ AD_ENTRY1s("data.dcp", "0134e92bcd5fd2837df3971087e96067", 163316498),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Actual Destination
+ {
+ "actualdest",
+ "",
+ AD_ENTRY1s("data.dcp", "6926f44b26f21ceb1d840eaab9aeb510", 9081740),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Chivalry is Not Dead
+ {
+ "chivalry",
+ "",
+ AD_ENTRY1s("data.dcp", "ebd0915d9a12df5224be22f53bb23eb6", 7278306),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Dead City (English)
+ {
+ "deadcity",
+ "",
+ {
+ {"english.dcp", 0, "c591046d6de7e381d76f70e0787b2b1f", 415935},
+ {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Dead City (Italian)
+ {
+ "deadcity",
+ "",
+ {
+ {"italian.dcp", 0, "92d8efb94436bec7bd1b7fe0b548192e", 454037},
+ {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205},
+ AD_LISTEND
+ },
+ Common::IT_ITA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Dead City (Russian)
+ {
+ "deadcity",
+ "",
+ {
+ {"russian.dcp", 0, "a0ae71e9e1185596fffb07ad2c951eb9", 653317},
+ {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205},
+ AD_LISTEND
+ },
+ Common::RU_RUS,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Dirty Split (English)
+ {
+ "dirtysplit",
+ "",
+ AD_ENTRY1s("data.dcp", "8f3dae199361ece0f59fb20cfff6eed3", 88577621),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Dirty Split (German)
+ {
+ "dirtysplit",
+ "",
+ AD_ENTRY1s("data.dcp", "139d8a25579e969f8b37d20e6e3de5f9", 92668291),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // East Side Story (Demo)
+ {
+ "eastside",
+ "Demo",
+ AD_ENTRY1s("data.dcp", "b3f8b09bb4b05ee3e9d14697525257f9", 59296246),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE |
+ ADGF_DEMO,
+ GUIO0()
+ },
+ // Ghosts in the Sheet
+ {
+ "ghostsheet",
+ "Demo",
+ AD_ENTRY1s("data.dcp", "dc1f6595f412ac25a52eaf47dad4ab81", 169083),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE |
+ ADGF_DEMO,
+ GUIO0()
+ },
+ // Hamlet or the last game without MMORPS features, shaders and product placement
+ {
+ "hamlet",
+ "",
+ AD_ENTRY1s("data.dcp", "f624add957a77c9930529fb28cc2450f", 88183022),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // J.U.L.I.A. (English)
+ {
+ "julia",
+ "",
+ AD_ENTRY1s("data.dcp", "c2264b4f8fcd132d2913ff5b6076a24f", 10109741),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // J.U.L.I.A. (English) (Demo)
+ {
+ "julia",
+ "Demo",
+ AD_ENTRY1s("data.dcp", "f0bbc3394555a9811f6050dae428cab6", 7655237),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE |
+ ADGF_DEMO,
+ GUIO0()
+ },
+ // Mirage
+ {
+ "mirage",
+ "",
+ AD_ENTRY1s("data.dcp", "d230b0b99c0aa77b9ecd094d8ee5573b", 17844056),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Pigeons in the Park
+ {
+ "pigeons",
+ "",
+ AD_ENTRY1s("data.dcp", "9143a5b6ff8206aefe3c4c643add3ec7", 2611100),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Reversion
+ {
+ "reversion",
+ "",
+ AD_ENTRY1s("data.dcp", "cd616f98ebfd047e0c540b50b4b70761", 254384531),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // Rosemary
+ {
+ "rosemary",
+ "",
+ AD_ENTRY1s("data.dcp", "4f2631138bd4d27587d9043f8aeff3df", 29483643),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // The Box
+ {
+ "thebox",
+ "",
+ AD_ENTRY1s("data.dcp", "ec5f0c7e8174e307701447b53afe7e2f", 108372483),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ // the white chamber (multi-language)
+ {
+ "twc",
+ "",
+ AD_ENTRY1s("data.dcp", "0011d01142547c61e51ba24dc42b579e", 186451273),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO0()
+ },
+ AD_TABLE_END_MARKER
+};
+
+} // End of namespace Wintermute
diff --git a/engines/wintermute/graphics/transparent_surface.cpp b/engines/wintermute/graphics/transparent_surface.cpp
new file mode 100644
index 0000000000..9319899495
--- /dev/null
+++ b/engines/wintermute/graphics/transparent_surface.cpp
@@ -0,0 +1,440 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "common/algorithm.h"
+#include "common/endian.h"
+#include "common/util.h"
+#include "common/rect.h"
+#include "common/textconsole.h"
+#include "graphics/primitives.h"
+#include "engines/wintermute/graphics/transparent_surface.h"
+
+namespace Wintermute {
+
+byte *TransparentSurface::_lookup = NULL;
+
+void TransparentSurface::destroyLookup() {
+ delete _lookup;
+ _lookup = NULL;
+}
+
+TransparentSurface::TransparentSurface() : Surface(), _enableAlphaBlit(true) {}
+
+TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Surface(), _enableAlphaBlit(true) {
+ if (copyData) {
+ copyFrom(surf);
+ } else {
+ w = surf.w;
+ h = surf.h;
+ pitch = surf.pitch;
+ format = surf.format;
+ pixels = surf.pixels;
+ }
+}
+
+void doBlitOpaque(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) {
+ byte *in, *out;
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ const int aIndex = 3;
+#else
+ const int aIndex = 0;
+#endif
+
+ for (uint32 i = 0; i < height; i++) {
+ out = outo;
+ in = ino;
+ memcpy(out, in, width * 4);
+ for (uint32 j = 0; j < width; j++) {
+ out[aIndex] = 0xFF;
+ out += 4;
+ }
+ outo += pitch;
+ ino += inoStep;
+ }
+}
+
+void TransparentSurface::generateLookup() {
+ _lookup = new byte[256 * 256];
+ for (int i = 0; i < 256; i++) {
+ for (int j = 0; j < 256; j++) {
+ _lookup[(i << 8) + j] = (i * j) >> 8;
+ }
+ }
+}
+
+void TransparentSurface::doBlitAlpha(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) {
+ byte *in, *out;
+
+ if (!_lookup) {
+ generateLookup();
+ }
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ const int aIndex = 3;
+ const int bIndex = 0;
+ const int gIndex = 1;
+ const int rIndex = 2;
+#else
+ const int aIndex = 0;
+ const int bIndex = 3;
+ const int gIndex = 2;
+ const int rIndex = 1;
+#endif
+
+ const int bShift = 0;//img->format.bShift;
+ const int gShift = 8;//img->format.gShift;
+ const int rShift = 16;//img->format.rShift;
+ const int aShift = 24;//img->format.aShift;
+
+ const int bShiftTarget = 0;//target.format.bShift;
+ const int gShiftTarget = 8;//target.format.gShift;
+ const int rShiftTarget = 16;//target.format.rShift;
+
+ for (uint32 i = 0; i < height; i++) {
+ out = outo;
+ in = ino;
+ for (uint32 j = 0; j < width; j++) {
+ uint32 pix = *(uint32 *)in;
+ uint32 oPix = *(uint32 *) out;
+ int b = (pix >> bShift) & 0xff;
+ int g = (pix >> gShift) & 0xff;
+ int r = (pix >> rShift) & 0xff;
+ int a = (pix >> aShift) & 0xff;
+ int outb, outg, outr, outa;
+ in += inStep;
+
+ switch (a) {
+ case 0: // Full transparency
+ out += 4;
+ break;
+ case 255: // Full opacity
+ outb = b;
+ outg = g;
+ outr = r;
+ outa = a;
+
+ out[aIndex] = outa;
+ out[bIndex] = outb;
+ out[gIndex] = outg;
+ out[rIndex] = outr;
+ out += 4;
+ break;
+
+ default: // alpha blending
+ outa = 255;
+
+ outb = _lookup[(((oPix >> bShiftTarget) & 0xff)) + ((255 - a) << 8)];
+ outg = _lookup[(((oPix >> gShiftTarget) & 0xff)) + ((255 - a) << 8)];
+ outr = _lookup[(((oPix >> rShiftTarget) & 0xff)) + ((255 - a) << 8)];
+ outb += _lookup[b + (a << 8)];
+ outg += _lookup[g + (a << 8)];
+ outr += _lookup[r + (a << 8)];
+
+ out[aIndex] = outa;
+ out[bIndex] = outb;
+ out[gIndex] = outg;
+ out[rIndex] = outr;
+ out += 4;
+ }
+ }
+ outo += pitch;
+ ino += inoStep;
+ }
+}
+
+
+Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height) {
+ int ca = (color >> 24) & 0xff;
+
+ Common::Rect retSize;
+ retSize.top = 0;
+ retSize.left = 0;
+ retSize.setWidth(0);
+ retSize.setHeight(0);
+ // Check if we need to draw anything at all
+ if (ca == 0)
+ return retSize;
+
+ int cr = (color >> 16) & 0xff;
+ int cg = (color >> 8) & 0xff;
+ int cb = (color >> 0) & 0xff;
+
+ // Compensate for transparency. Since we're coming
+ // down to 255 alpha, we just compensate for the colors here
+ if (ca != 255) {
+ cr = cr * ca >> 8;
+ cg = cg * ca >> 8;
+ cb = cb * ca >> 8;
+ }
+
+ // Create an encapsulating surface for the data
+ TransparentSurface srcImage(*this, false);
+ // TODO: Is the data really in the screen format?
+ if (format.bytesPerPixel != 4) {
+ warning("TransparentSurface can only blit 32 bpp images");
+ return retSize;
+ }
+
+ if (pPartRect) {
+ srcImage.pixels = &((char *)pixels)[pPartRect->top * srcImage.pitch + pPartRect->left * 4];
+ srcImage.w = pPartRect->width();
+ srcImage.h = pPartRect->height();
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
+ pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
+ } else {
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
+ srcImage.w, srcImage.h, color, width, height);
+ }
+
+ if (width == -1)
+ width = srcImage.w;
+ if (height == -1)
+ height = srcImage.h;
+
+#ifdef SCALING_TESTING
+ // Hardcode scaling to 66% to test scaling
+ width = width * 2 / 3;
+ height = height * 2 / 3;
+#endif
+
+ Graphics::Surface *img;
+ Graphics::Surface *imgScaled = NULL;
+ byte *savedPixels = NULL;
+ if ((width != srcImage.w) || (height != srcImage.h)) {
+ // Scale the image
+ img = imgScaled = srcImage.scale(width, height);
+ savedPixels = (byte *)img->pixels;
+ } else {
+ img = &srcImage;
+ }
+
+ // Handle off-screen clipping
+ if (posY < 0) {
+ img->h = MAX(0, (int)img->h - -posY);
+ img->pixels = (byte *)img->pixels + img->pitch * -posY;
+ posY = 0;
+ }
+
+ if (posX < 0) {
+ img->w = MAX(0, (int)img->w - -posX);
+ img->pixels = (byte *)img->pixels + (-posX * 4);
+ posX = 0;
+ }
+
+ img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0));
+ img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0));
+
+ if ((img->w > 0) && (img->h > 0)) {
+ int xp = 0, yp = 0;
+
+ int inStep = 4;
+ int inoStep = img->pitch;
+ if (flipping & TransparentSurface::FLIP_V) {
+ inStep = -inStep;
+ xp = img->w - 1;
+ }
+
+ if (flipping & TransparentSurface::FLIP_H) {
+ inoStep = -inoStep;
+ yp = img->h - 1;
+ }
+
+ byte *ino = (byte *)img->getBasePtr(xp, yp);
+ byte *outo = (byte *)target.getBasePtr(posX, posY);
+ byte *in, *out;
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ const int aIndex = 3;
+ const int bIndex = 0;
+ const int gIndex = 1;
+ const int rIndex = 2;
+#else
+ const int aIndex = 0;
+ const int bIndex = 3;
+ const int gIndex = 2;
+ const int rIndex = 1;
+#endif
+ const int bShift = 0;//img->format.bShift;
+ const int gShift = 8;//img->format.gShift;
+ const int rShift = 16;//img->format.rShift;
+ const int aShift = 24;//img->format.aShift;
+
+ const int bShiftTarget = 0;//target.format.bShift;
+ const int gShiftTarget = 8;//target.format.gShift;
+ const int rShiftTarget = 16;//target.format.rShift;
+
+ if (ca == 255 && cb == 255 && cg == 255 && cr == 255) {
+ if (_enableAlphaBlit) {
+ doBlitAlpha(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
+ } else {
+ doBlitOpaque(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
+ }
+ } else {
+ for (int i = 0; i < img->h; i++) {
+ out = outo;
+ in = ino;
+ for (int j = 0; j < img->w; j++) {
+ uint32 pix = *(uint32 *)in;
+ uint32 o_pix = *(uint32 *) out;
+ int b = (pix >> bShift) & 0xff;
+ int g = (pix >> gShift) & 0xff;
+ int r = (pix >> rShift) & 0xff;
+ int a = (pix >> aShift) & 0xff;
+ int outb, outg, outr, outa;
+ in += inStep;
+
+ if (ca != 255) {
+ a = a * ca >> 8;
+ }
+
+ switch (a) {
+ case 0: // Full transparency
+ out += 4;
+ break;
+ case 255: // Full opacity
+ if (cb != 255)
+ outb = (b * cb) >> 8;
+ else
+ outb = b;
+
+ if (cg != 255)
+ outg = (g * cg) >> 8;
+ else
+ outg = g;
+
+ if (cr != 255)
+ outr = (r * cr) >> 8;
+ else
+ outr = r;
+ outa = a;
+ out[aIndex] = outa;
+ out[bIndex] = outb;
+ out[gIndex] = outg;
+ out[rIndex] = outr;
+ out += 4;
+ break;
+
+ default: // alpha blending
+ outa = 255;
+ outb = (o_pix >> bShiftTarget) & 0xff;
+ outg = (o_pix >> gShiftTarget) & 0xff;
+ outr = (o_pix >> rShiftTarget) & 0xff;
+ if (cb == 0)
+ outb = 0;
+ else if (cb != 255)
+ outb += ((b - outb) * a * cb) >> 16;
+ else
+ outb += ((b - outb) * a) >> 8;
+ if (cg == 0)
+ outg = 0;
+ else if (cg != 255)
+ outg += ((g - outg) * a * cg) >> 16;
+ else
+ outg += ((g - outg) * a) >> 8;
+ if (cr == 0)
+ outr = 0;
+ else if (cr != 255)
+ outr += ((r - outr) * a * cr) >> 16;
+ else
+ outr += ((r - outr) * a) >> 8;
+ out[aIndex] = outa;
+ out[bIndex] = outb;
+ out[gIndex] = outg;
+ out[rIndex] = outr;
+ out += 4;
+ }
+ }
+ outo += target.pitch;
+ ino += inoStep;
+ }
+ }
+ }
+
+ if (imgScaled) {
+ imgScaled->pixels = savedPixels;
+ imgScaled->free();
+ delete imgScaled;
+ }
+
+ retSize.setWidth(img->w);
+ retSize.setHeight(img->h);
+ return retSize;
+}
+
+TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const {
+ Common::Rect srcRect(0, 0, (int16)w, (int16)h);
+ Common::Rect dstRect(0, 0, (int16)newWidth, (int16)newHeight);
+ return scale(srcRect, dstRect);
+}
+
+// Copied from clone2727's https://github.com/clone2727/scummvm/blob/pegasus/engines/pegasus/surface.cpp#L247
+TransparentSurface *TransparentSurface::scale(const Common::Rect &srcRect, const Common::Rect &dstRect) const {
+ // I'm doing simple linear scaling here
+ // dstRect(x, y) = srcRect(x * srcW / dstW, y * srcH / dstH);
+ TransparentSurface *target = new TransparentSurface();
+
+ int srcW = srcRect.width();
+ int srcH = srcRect.height();
+ int dstW = dstRect.width();
+ int dstH = dstRect.height();
+
+ target->create((uint16)dstW, (uint16)dstH, this->format);
+
+ for (int y = 0; y < dstH; y++) {
+ for (int x = 0; x < dstW; x++) {
+ uint32 color = READ_UINT32((const byte *)getBasePtr(x * srcW / dstW + srcRect.left,
+ y * srcH / dstH + srcRect.top));
+ WRITE_UINT32((byte *)target->getBasePtr(x + dstRect.left, y + dstRect.top), color);
+ }
+ }
+ return target;
+
+}
+
+/**
+ * Writes a color key to the alpha channel of the surface
+ * @param rKey the red component of the color key
+ * @param gKey the green component of the color key
+ * @param bKey the blue component of the color key
+ * @param overwriteAlpha if true, all other alpha will be set fully opaque
+ */
+void TransparentSurface::applyColorKey(uint8 rKey, uint8 gKey, uint8 bKey, bool overwriteAlpha) {
+ assert(format.bytesPerPixel == 4);
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ uint32 pix = ((uint32 *)pixels)[i * w + j];
+ uint8 r, g, b, a;
+ format.colorToARGB(pix, a, r, g, b);
+ if (r == rKey && g == gKey && b == bKey) {
+ a = 0;
+ ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b);
+ } else if (overwriteAlpha) {
+ a = 255;
+ ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b);
+ }
+ }
+ }
+}
+
+} // End of namespace Graphics
diff --git a/engines/wintermute/graphics/transparent_surface.h b/engines/wintermute/graphics/transparent_surface.h
new file mode 100644
index 0000000000..8b00dccbd9
--- /dev/null
+++ b/engines/wintermute/graphics/transparent_surface.h
@@ -0,0 +1,131 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef GRAPHICS_TRANSPARENTSURFACE_H
+#define GRAPHICS_TRANSPARENTSURFACE_H
+
+#include "graphics/surface.h"
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// TODO: Find a better solution for this.
+#define BS_RGB(R,G,B) (0xFF000000 | ((R) << 16) | ((G) << 8) | (B))
+#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B))
+
+namespace Wintermute {
+
+/**
+ * A transparent graphics surface, which implements alpha blitting.
+ */
+struct TransparentSurface : public Graphics::Surface {
+ TransparentSurface();
+ TransparentSurface(const Graphics::Surface &surf, bool copyData = false);
+
+ void setColorKey(char r, char g, char b);
+ void disableColorKey();
+
+ // Enums
+ /**
+ @brief The possible flipping parameters for the blit methode.
+ */
+ enum FLIP_FLAGS {
+ /// The image will not be flipped.
+ FLIP_NONE = 0,
+ /// The image will be flipped at the horizontal axis.
+ FLIP_H = 1,
+ /// The image will be flipped at the vertical axis.
+ FLIP_V = 2,
+ /// The image will be flipped at the horizontal and vertical axis.
+ FLIP_HV = FLIP_H | FLIP_V,
+ /// The image will be flipped at the horizontal and vertical axis.
+ FLIP_VH = FLIP_H | FLIP_V
+ };
+
+ bool _enableAlphaBlit;
+
+ /**
+ @brief renders the surface to another surface
+ @param pDest a pointer to the target image. In most cases this is the framebuffer.
+ @param PosX the position on the X-axis in the target image in pixels where the image is supposed to be rendered.<br>
+ The default value is 0.
+ @param PosY the position on the Y-axis in the target image in pixels where the image is supposed to be rendered.<br>
+ The default value is 0.
+ @param Flipping how the the image should be flipped.<br>
+ The default value is BS_Image::FLIP_NONE (no flipping)
+ @param pSrcPartRect Pointer on Common::Rect which specifies the section to be rendered. If the whole image has to be rendered the Pointer is NULL.<br>
+ This referes to the unflipped and unscaled image.<br>
+ The default value is NULL.
+ @param Color an ARGB color value, which determines the parameters for the color modulation und alpha blending.<br>
+ The alpha component of the color determines the alpha blending parameter (0 = no covering, 255 = full covering).<br>
+ The color components determines the color for color modulation.<br>
+ The default value is BS_ARGB(255, 255, 255, 255) (full covering, no color modulation).
+ The macros BS_RGB and BS_ARGB can be used for the creation of the color value.
+ @param Width the output width of the screen section.
+ The images will be scaled if the output width of the screen section differs from the image section.<br>
+ The value -1 determines that the image should not be scaled.<br>
+ The default value is -1.
+ @param Width the output height of the screen section.
+ The images will be scaled if the output width of the screen section differs from the image section.<br>
+ The value -1 determines that the image should not be scaled.<br>
+ The default value is -1.
+ @return returns false if the rendering failed.
+ */
+
+ Common::Rect blit(Graphics::Surface &target, int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1);
+ void applyColorKey(uint8 r, uint8 g, uint8 b, bool overwriteAlpha = false);
+ // The following scale-code supports arbitrary scaling (i.e. no repeats of column 0 at the end of lines)
+ TransparentSurface *scale(uint16 newWidth, uint16 newHeight) const;
+ TransparentSurface *scale(const Common::Rect &srcRect, const Common::Rect &dstRect) const;
+ static byte *_lookup;
+ static void destroyLookup();
+private:
+ static void doBlitAlpha(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep);
+ static void generateLookup();
+};
+
+/**
+ * A deleter for Surface objects which can be used with SharedPtr.
+ *
+ * This deleter assures Surface::free is called on deletion.
+ */
+/*struct SharedPtrTransparentSurfaceDeleter {
+ void operator()(TransparentSurface *ptr) {
+ ptr->free();
+ delete ptr;
+ }
+};*/
+
+
+} // End of namespace Graphics
+
+
+#endif
diff --git a/engines/wintermute/math/math_util.cpp b/engines/wintermute/math/math_util.cpp
new file mode 100644
index 0000000000..31af77538a
--- /dev/null
+++ b/engines/wintermute/math/math_util.cpp
@@ -0,0 +1,52 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/math/math_util.h"
+#include <math.h>
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+float MathUtil::round(float val) {
+ float result = floor(val);
+ if (val - result >= 0.5f) {
+ result += 1.0;
+ }
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////////
+float MathUtil::roundUp(float val) {
+ float result = floor(val);
+ if (val - result > 0) {
+ result += 1.0;
+ }
+ return result;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/math/math_util.h b/engines/wintermute/math/math_util.h
new file mode 100644
index 0000000000..38b6d9abf9
--- /dev/null
+++ b/engines/wintermute/math/math_util.h
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_MATHUTIL_H
+#define WINTERMUTE_MATHUTIL_H
+
+namespace Wintermute {
+
+class MathUtil {
+public:
+ static float round(float val);
+ static float roundUp(float val);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/math/matrix4.cpp b/engines/wintermute/math/matrix4.cpp
new file mode 100644
index 0000000000..ca1eae8813
--- /dev/null
+++ b/engines/wintermute/math/matrix4.cpp
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/math/matrix4.h"
+#include "engines/wintermute/math/vector2.h"
+#include <math.h>
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+Matrix4::Matrix4() {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ m[i][j] = 0.0f;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+Matrix4::~Matrix4() {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void Matrix4::identity() {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ m[i][j] = 0.0f;
+ }
+ }
+ m[0][0] = 1.0f;
+ m[1][1] = 1.0f;
+ m[2][2] = 1.0f;
+ m[3][3] = 1.0f;
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Matrix4::rotationZ(float angle) {
+ identity();
+
+ m[0][0] = cos(angle);
+ m[1][1] = cos(angle);
+ m[0][1] = sin(angle);
+ m[1][0] = -sin(angle);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Matrix4::transformVector2(Vector2 &vec) {
+ float norm;
+
+ norm = m[0][3] * vec.x + m[1][3] * vec.y + m[3][3];
+
+ float x = (m[0][0] * vec.x + m[1][0] * vec.y + m[3][0]) / norm;
+ float y = (m[0][1] * vec.x + m[1][1] * vec.y + m[3][1]) / norm;
+
+ vec.x = x;
+ vec.y = y;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/math/matrix4.h b/engines/wintermute/math/matrix4.h
new file mode 100644
index 0000000000..273633f723
--- /dev/null
+++ b/engines/wintermute/math/matrix4.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_MATRIX4_H
+#define WINTERMUTE_MATRIX4_H
+
+namespace Wintermute {
+
+class Vector2;
+
+class Matrix4 {
+public:
+ Matrix4();
+ ~Matrix4();
+
+ void identity();
+ void rotationZ(float angle);
+ void transformVector2(Vector2 &vec);
+
+ float m[4][4];
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/math/rect32.h b/engines/wintermute/math/rect32.h
new file mode 100644
index 0000000000..190c1135cf
--- /dev/null
+++ b/engines/wintermute/math/rect32.h
@@ -0,0 +1,94 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WINTERMUTE_RECT32_H
+#define WINTERMUTE_RECT32_H
+
+#include "common/system.h"
+
+namespace Wintermute {
+
+struct Point32 {
+ int32 x;
+ int32 y;
+};
+
+struct Rect32 {
+ int32 top, left; ///< The point at the top left of the rectangle (part of the rect).
+ int32 bottom, right; ///< The point at the bottom right of the rectangle (not part of the rect).
+
+ Rect32() : top(0), left(0), bottom(0), right(0) {}
+ Rect32(int32 w, int32 h) : top(0), left(0), bottom(h), right(w) {}
+ Rect32(int32 x1, int32 y1, int32 x2, int32 y2) : top(y1), left(x1), bottom(y2), right(x2) {
+ assert(isValidRect());
+ }
+ bool operator==(const Rect32 &rhs) const {
+ return equals(rhs);
+ }
+ bool operator!=(const Rect32 &rhs) const {
+ return !equals(rhs);
+ }
+
+ int32 width() const {
+ return right - left;
+ }
+ int32 height() const {
+ return bottom - top;
+ }
+
+ void setWidth(int32 aWidth) {
+ right = left + aWidth;
+ }
+
+ void setHeight(int32 aHeight) {
+ bottom = top + aHeight;
+ }
+
+ void setEmpty() {
+ left = right = top = bottom = 0;
+ }
+
+ void offsetRect(int dx, int dy) {
+ left += dx;
+ top += dy;
+ right += dx;
+ bottom += dy;
+ }
+ /**
+ * Check if the given rect is equal to this one.
+ *
+ * @param r The rectangle to check
+ *
+ * @return true if the given rect is equal, false otherwise
+ */
+ bool equals(const Rect32 &r) const {
+ return (left == r.left) && (right == r.right) && (top == r.top) && (bottom == r.bottom);
+ }
+
+ bool isValidRect() const {
+ return (left <= right && top <= bottom);
+ }
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/math/vector2.cpp b/engines/wintermute/math/vector2.cpp
new file mode 100644
index 0000000000..74c5586d62
--- /dev/null
+++ b/engines/wintermute/math/vector2.cpp
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/math/vector2.h"
+#include <math.h>
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+Vector2::Vector2() {
+ x = y = 0.0f;
+}
+
+//////////////////////////////////////////////////////////////////////////
+Vector2::Vector2(float xVal, float yVal) {
+ this->x = xVal;
+ this->y = yVal;
+}
+
+//////////////////////////////////////////////////////////////////////////
+Vector2::~Vector2() {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float Vector2::length() const {
+ return (float)sqrt(x * x + y * y);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/math/vector2.h b/engines/wintermute/math/vector2.h
new file mode 100644
index 0000000000..31f31daaa0
--- /dev/null
+++ b/engines/wintermute/math/vector2.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_VECTOR2_H
+#define WINTERMUTE_VECTOR2_H
+
+namespace Wintermute {
+
+class Vector2 {
+public:
+ Vector2();
+ Vector2(float x, float y);
+ ~Vector2();
+
+ float length() const;
+
+ inline Vector2 &operator= (const Vector2 &other) {
+ x = other.x;
+ y = other.y;
+
+ return *this;
+ }
+
+ inline Vector2 operator+ (const Vector2 &other) const {
+ return Vector2(x + other.x, y + other.y);
+ }
+
+ inline Vector2 operator- (const Vector2 &other) const {
+ return Vector2(x - other.x, y - other.y);
+ }
+
+ inline Vector2 operator* (const float scalar) const {
+ return Vector2(x * scalar, y * scalar);
+ }
+
+ inline Vector2 &operator+= (const Vector2 &other) {
+ x += other.x;
+ y += other.y;
+
+ return *this;
+ }
+
+
+ float x;
+ float y;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
new file mode 100644
index 0000000000..7b5b1b1a3a
--- /dev/null
+++ b/engines/wintermute/module.mk
@@ -0,0 +1,123 @@
+MODULE := engines/wintermute
+
+MODULE_OBJS := \
+ ad/ad_actor.o \
+ ad/ad_entity.o \
+ ad/ad_game.o \
+ ad/ad_inventory.o \
+ ad/ad_inventory_box.o \
+ ad/ad_item.o \
+ ad/ad_layer.o \
+ ad/ad_node_state.o \
+ ad/ad_object.o \
+ ad/ad_path.o \
+ ad/ad_path_point.o \
+ ad/ad_region.o \
+ ad/ad_response.o \
+ ad/ad_response_box.o \
+ ad/ad_response_context.o \
+ ad/ad_rot_level.o \
+ ad/ad_scale_level.o \
+ ad/ad_scene.o \
+ ad/ad_scene_node.o \
+ ad/ad_scene_state.o \
+ ad/ad_sentence.o \
+ ad/ad_sprite_set.o \
+ ad/ad_talk_def.o \
+ ad/ad_talk_holder.o \
+ ad/ad_talk_node.o \
+ ad/ad_waypoint_group.o \
+ base/scriptables/script.o \
+ base/scriptables/script_engine.o \
+ base/scriptables/script_stack.o \
+ base/scriptables/script_value.o \
+ base/scriptables/script_ext_array.o \
+ base/scriptables/script_ext_date.o \
+ base/scriptables/script_ext_file.o \
+ base/scriptables/script_ext_math.o \
+ base/scriptables/script_ext_object.o \
+ base/scriptables/script_ext_mem_buffer.o \
+ base/scriptables/script_ext_string.o \
+ base/file/base_disk_file.o \
+ base/file/base_file.o \
+ base/file/base_file_entry.o \
+ base/file/base_package.o \
+ base/file/base_resources.o \
+ base/file/base_save_thumb_file.o \
+ base/font/base_font_bitmap.o \
+ base/font/base_font_truetype.o \
+ base/font/base_font.o \
+ base/font/base_font_storage.o \
+ base/gfx/base_image.o \
+ base/gfx/base_renderer.o \
+ base/gfx/base_surface.o \
+ base/gfx/osystem/base_surface_osystem.o \
+ base/gfx/osystem/base_render_osystem.o \
+ base/particles/part_particle.o \
+ base/particles/part_emitter.o \
+ base/particles/part_force.o \
+ base/sound/base_sound.o \
+ base/sound/base_sound_buffer.o \
+ base/sound/base_sound_manager.o \
+ base/base_active_rect.o \
+ base/base.o \
+ base/base_dynamic_buffer.o \
+ base/base_engine.o \
+ base/base_fader.o \
+ base/base_file_manager.o \
+ base/base_frame.o \
+ base/base_game.o \
+ base/base_keyboard_state.o \
+ base/base_named_object.o \
+ base/base_object.o \
+ base/base_parser.o \
+ base/base_persistence_manager.o \
+ base/base_point.o \
+ base/base_quick_msg.o \
+ base/base_region.o \
+ base/base_save_thumb_helper.o \
+ base/base_scriptable.o \
+ base/base_script_holder.o \
+ base/base_sprite.o \
+ base/base_string_table.o \
+ base/base_sub_frame.o \
+ base/base_surface_storage.o \
+ base/base_transition_manager.o \
+ base/base_viewport.o \
+ base/saveload.o \
+ detection.o \
+ graphics/transparent_surface.o \
+ math/math_util.o \
+ math/matrix4.o \
+ math/vector2.o \
+ platform_osystem.o \
+ system/sys_class.o \
+ system/sys_class_registry.o \
+ system/sys_instance.o \
+ ui/ui_button.o \
+ ui/ui_edit.o \
+ ui/ui_entity.o \
+ ui/ui_object.o \
+ ui/ui_text.o \
+ ui/ui_tiled_image.o \
+ ui/ui_window.o \
+ utils/convert_utf.o \
+ utils/crc.o \
+ utils/path_util.o \
+ utils/string_util.o \
+ utils/utils.o \
+ video/video_player.o \
+ video/video_theora_player.o \
+ wintermute.o \
+ persistent.o
+
+MODULE_DIRS += \
+ engines/wintermute
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_WINTERMUTE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/wintermute/persistent.cpp b/engines/wintermute/persistent.cpp
new file mode 100644
index 0000000000..514fd61d34
--- /dev/null
+++ b/engines/wintermute/persistent.cpp
@@ -0,0 +1,168 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_actor.h"
+#include "engines/wintermute/ad/ad_entity.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_inventory.h"
+#include "engines/wintermute/ad/ad_inventory_box.h"
+#include "engines/wintermute/ad/ad_item.h"
+#include "engines/wintermute/ad/ad_layer.h"
+#include "engines/wintermute/ad/ad_node_state.h"
+#include "engines/wintermute/ad/ad_object.h"
+#include "engines/wintermute/ad/ad_path.h"
+#include "engines/wintermute/ad/ad_path_point.h"
+#include "engines/wintermute/ad/ad_region.h"
+#include "engines/wintermute/ad/ad_response.h"
+#include "engines/wintermute/ad/ad_response_box.h"
+#include "engines/wintermute/ad/ad_response_context.h"
+#include "engines/wintermute/ad/ad_rot_level.h"
+#include "engines/wintermute/ad/ad_scale_level.h"
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/ad/ad_scene_node.h"
+#include "engines/wintermute/ad/ad_scene_state.h"
+#include "engines/wintermute/ad/ad_sentence.h"
+#include "engines/wintermute/ad/ad_sprite_set.h"
+#include "engines/wintermute/ad/ad_talk_def.h"
+#include "engines/wintermute/ad/ad_talk_holder.h"
+#include "engines/wintermute/ad/ad_talk_node.h"
+#include "engines/wintermute/ad/ad_waypoint_group.h"
+#include "engines/wintermute/base/base_fader.h"
+#include "engines/wintermute/base/font/base_font_bitmap.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/font/base_font_truetype.h"
+#include "engines/wintermute/base/base_frame.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_keyboard_state.h"
+#include "engines/wintermute/base/base_object.h"
+#include "engines/wintermute/base/base_point.h"
+#include "engines/wintermute/base/base_region.h"
+#include "engines/wintermute/base/base_scriptable.h"
+#include "engines/wintermute/base/base_script_holder.h"
+#include "engines/wintermute/base/sound/base_sound.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/base_viewport.h"
+#include "engines/wintermute/base/particles/part_emitter.h"
+#include "engines/wintermute/base/scriptables/script_engine.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_ext_array.h"
+#include "engines/wintermute/base/scriptables/script_ext_date.h"
+#include "engines/wintermute/base/scriptables/script_ext_file.h"
+#include "engines/wintermute/base/scriptables/script_ext_math.h"
+#include "engines/wintermute/base/scriptables/script_ext_mem_buffer.h"
+#include "engines/wintermute/base/scriptables/script_ext_object.h"
+#include "engines/wintermute/base/scriptables/script_ext_string.h"
+#include "engines/wintermute/ui/ui_button.h"
+#include "engines/wintermute/ui/ui_edit.h"
+#include "engines/wintermute/ui/ui_entity.h"
+#include "engines/wintermute/ui/ui_text.h"
+#include "engines/wintermute/ui/ui_tiled_image.h"
+#include "engines/wintermute/ui/ui_window.h"
+#include "engines/wintermute/video/video_theora_player.h"
+#include "engines/wintermute/system/sys_class.h"
+
+// SystemClass adds these objects to the registry, thus they aren't as leaked as they look
+#define REGISTER_CLASS(class_name, persistent_class)\
+ new Wintermute::SystemClass(class_name::_className, class_name::persistBuild, class_name::persistLoad, persistent_class);
+
+namespace Wintermute {
+
+// This is done in a separate file, to avoid including the kitchensink in SystemClassRegistry.
+void SystemClassRegistry::registerClasses() {
+ REGISTER_CLASS(AdActor, false)
+ REGISTER_CLASS(AdEntity, false)
+ REGISTER_CLASS(AdGame, true)
+ REGISTER_CLASS(AdInventory, false)
+ REGISTER_CLASS(AdInventoryBox, false)
+ REGISTER_CLASS(AdItem, false)
+ REGISTER_CLASS(AdLayer, false)
+ REGISTER_CLASS(AdNodeState, false)
+ REGISTER_CLASS(AdObject, false)
+ REGISTER_CLASS(AdPath, false)
+ REGISTER_CLASS(AdPathPoint, false)
+ REGISTER_CLASS(AdRegion, false)
+ REGISTER_CLASS(AdResponse, false)
+ REGISTER_CLASS(AdResponseBox, false)
+ REGISTER_CLASS(AdResponseContext, false)
+ REGISTER_CLASS(AdRotLevel, false)
+ REGISTER_CLASS(AdScaleLevel, false)
+ REGISTER_CLASS(AdScene, false)
+ REGISTER_CLASS(AdSceneNode, false)
+ REGISTER_CLASS(AdSceneState, false)
+ REGISTER_CLASS(AdSentence, false)
+ REGISTER_CLASS(AdSpriteSet, false)
+ REGISTER_CLASS(AdTalkDef, false)
+ REGISTER_CLASS(AdTalkHolder, false)
+ REGISTER_CLASS(AdTalkNode, false)
+ REGISTER_CLASS(AdWaypointGroup, false)
+
+ REGISTER_CLASS(BaseFader, false)
+ REGISTER_CLASS(BaseFont, false)
+ REGISTER_CLASS(BaseFontBitmap, false)
+ REGISTER_CLASS(BaseFontStorage, true)
+ REGISTER_CLASS(BaseFontTT, false)
+ REGISTER_CLASS(BaseFrame, false)
+ REGISTER_CLASS(BaseGame, true)
+ REGISTER_CLASS(BaseKeyboardState, false)
+ REGISTER_CLASS(BaseObject, false)
+ REGISTER_CLASS(BasePoint, false)
+ REGISTER_CLASS(BaseRegion, false)
+ REGISTER_CLASS(BaseScriptable, false)
+ REGISTER_CLASS(BaseScriptHolder, false)
+ REGISTER_CLASS(BaseSound, false)
+ REGISTER_CLASS(BaseSprite, false)
+ REGISTER_CLASS(BaseSubFrame, false)
+
+ REGISTER_CLASS(BaseViewport, false)
+ REGISTER_CLASS(PartEmitter, false)
+ REGISTER_CLASS(ScEngine, true)
+ REGISTER_CLASS(ScScript, false)
+ REGISTER_CLASS(ScStack, false)
+ REGISTER_CLASS(ScValue, false)
+ REGISTER_CLASS(SXArray, false)
+ REGISTER_CLASS(SXDate, false)
+ REGISTER_CLASS(SXFile, false)
+ REGISTER_CLASS(SXMath, true)
+ REGISTER_CLASS(SXMemBuffer, false)
+ REGISTER_CLASS(SXObject, false)
+ REGISTER_CLASS(SXString, false)
+
+ REGISTER_CLASS(UIButton, false)
+ REGISTER_CLASS(UIEdit, false)
+ REGISTER_CLASS(UIEntity, false)
+ REGISTER_CLASS(UIObject, false)
+ REGISTER_CLASS(UIText, false)
+ REGISTER_CLASS(UITiledImage, false)
+ REGISTER_CLASS(UIWindow, false)
+ REGISTER_CLASS(VideoTheoraPlayer, false)
+}
+
+}
diff --git a/engines/wintermute/persistent.h b/engines/wintermute/persistent.h
new file mode 100644
index 0000000000..c862df5d6b
--- /dev/null
+++ b/engines/wintermute/persistent.h
@@ -0,0 +1,89 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_PERSISTENT_H
+#define WINTERMUTE_PERSISTENT_H
+
+namespace Wintermute {
+
+class BasePersistenceManager;
+
+// persistence support
+typedef void *(*PERSISTBUILD)(void);
+typedef bool(*PERSISTLOAD)(void *, BasePersistenceManager *);
+typedef void (*SYS_INSTANCE_CALLBACK)(void *Instance, void *Data);
+} // end of namespace Wintermute
+
+#include "engines/wintermute/system/sys_class_registry.h"
+namespace Wintermute {
+
+
+#define DECLARE_PERSISTENT(class_name, parent_class)\
+ static const char _className[];\
+ static void* persistBuild(void);\
+ virtual const char* getClassName();\
+ static bool persistLoad(void* Instance, BasePersistenceManager* PersistMgr);\
+ class_name(TDynamicConstructor p1, TDynamicConstructor p2) :parent_class(p1, p2){ /*memset(this, 0, sizeof(class_name));*/ };\
+ virtual bool persist(BasePersistenceManager* PersistMgr);\
+ void* operator new (size_t size);\
+ void operator delete(void* p);\
+
+
+#define IMPLEMENT_PERSISTENT(class_name, persistent_class)\
+ const char class_name::_className[] = #class_name;\
+ void* class_name::persistBuild(){\
+ return ::new class_name(DYNAMIC_CONSTRUCTOR, DYNAMIC_CONSTRUCTOR);\
+ }\
+ \
+ bool class_name::persistLoad(void* Instance, BasePersistenceManager* PersistMgr){\
+ return ((class_name*)Instance)->persist(PersistMgr);\
+ }\
+ \
+ const char* class_name::getClassName(){\
+ return #class_name;\
+ }\
+ \
+ /*SystemClass Register##class_name(class_name::_className, class_name::PersistBuild, class_name::PersistLoad, persistent_class);*/\
+ \
+ void* class_name::operator new (size_t size){\
+ void* ret = ::operator new(size);\
+ SystemClassRegistry::getInstance()->registerInstance(#class_name, ret);\
+ return ret;\
+ }\
+ \
+ void class_name::operator delete (void* p){\
+ SystemClassRegistry::getInstance()->unregisterInstance(#class_name, p);\
+ ::operator delete(p);\
+ }\
+
+#define TMEMBER(member_name) #member_name, &member_name
+#define TMEMBER_INT(member_name) #member_name, (int*)&member_name
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/platform_osystem.cpp b/engines/wintermute/platform_osystem.cpp
new file mode 100644
index 0000000000..0bd99b11cd
--- /dev/null
+++ b/engines/wintermute/platform_osystem.cpp
@@ -0,0 +1,262 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "common/str.h"
+#include "common/textconsole.h"
+#include "common/system.h"
+
+namespace Wintermute {
+
+BaseGame *BasePlatform::_gameRef = NULL;
+
+#define CLASS_NAME "GF_FRAME"
+int BasePlatform::initialize(BaseGame *inGame, int argc, char *argv[]) {
+ _gameRef = inGame;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BasePlatform::handleEvent(Common::Event *event) {
+ switch (event->type) {
+
+ case Common::EVENT_LBUTTONDOWN:
+ if (_gameRef) {
+ if (_gameRef->isLeftDoubleClick()) {
+ _gameRef->onMouseLeftDblClick();
+ } else {
+ _gameRef->onMouseLeftDown();
+ }
+ }
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ if (_gameRef) {
+ if (_gameRef->isRightDoubleClick()) {
+ _gameRef->onMouseRightDblClick();
+ } else {
+ _gameRef->onMouseRightDown();
+ }
+ }
+ break;
+ case Common::EVENT_MBUTTONDOWN:
+ if (_gameRef) {
+ _gameRef->onMouseMiddleDown();
+ }
+ break;
+ case Common::EVENT_LBUTTONUP:
+ if (_gameRef) {
+ _gameRef->onMouseLeftUp();
+ }
+ break;
+ case Common::EVENT_RBUTTONUP:
+ if (_gameRef) {
+ _gameRef->onMouseRightUp();
+ }
+ break;
+ case Common::EVENT_MBUTTONUP:
+ if (_gameRef) {
+ _gameRef->onMouseMiddleUp();
+ }
+ break;
+ case Common::EVENT_KEYDOWN:
+ if (_gameRef) {
+ _gameRef->handleKeypress(event);
+ }
+ break;
+ case Common::EVENT_KEYUP:
+ if (_gameRef) {
+ _gameRef->handleKeyRelease(event);
+ }
+ break;
+ case Common::EVENT_WHEELUP:
+ case Common::EVENT_WHEELDOWN:
+ if (_gameRef) {
+ _gameRef->handleMouseWheel(event->mouse.y);
+ }
+ break;
+// Focus-events have been removed (_gameRef->onActivate originally)
+ case Common::EVENT_RTL:
+ _gameRef->_quitting = true;
+ break;
+ case Common::EVENT_QUIT:
+// Block kept in case we want to support autoSaveOnExit.
+// Originally this was the behaviour for WME Lite on iOS:
+// if (_gameRef) {
+// _gameRef->AutoSaveOnExit();
+// _gameRef->_quitting = true;
+// }
+ if (_gameRef) {
+ _gameRef->onWindowClose();
+ }
+ break;
+ default:
+ // TODO: Do we care about any other events?
+ break;
+
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Win32 API bindings
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::getCursorPos(Point32 *lpPoint) {
+ BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer);
+
+ Common::Point p = g_system->getEventManager()->getMousePos();
+ lpPoint->x = p.x;
+ lpPoint->y = p.y;
+
+ renderer->pointFromScreen(lpPoint);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::setCursorPos(int x, int y) {
+ BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer);
+
+ Point32 p;
+ p.x = x;
+ p.y = y;
+ renderer->pointToScreen(&p);
+
+ g_system->warpMouse(x, y);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::showWindow(int nCmdShow) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BasePlatform::setCapture() {
+ return;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::releaseCapture() {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::setRectEmpty(Rect32 *lprc) {
+ lprc->left = lprc->right = lprc->top = lprc->bottom = 0;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::isRectEmpty(const Rect32 *lprc) {
+ return (lprc->left >= lprc->right) || (lprc->top >= lprc->bottom);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::ptInRect(Rect32 *lprc, Point32 p) {
+ return (p.x >= lprc->left) && (p.x < lprc->right) && (p.y >= lprc->top) && (p.y < lprc->bottom);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::setRect(Rect32 *lprc, int left, int top, int right, int bottom) {
+ lprc->left = left;
+ lprc->top = top;
+ lprc->right = right;
+ lprc->bottom = bottom;
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::intersectRect(Rect32 *lprcDst, const Rect32 *lprcSrc1, const Rect32 *lprcSrc2) {
+ if (isRectEmpty(lprcSrc1) || isRectEmpty(lprcSrc2) ||
+ lprcSrc1->left >= lprcSrc2->right || lprcSrc2->left >= lprcSrc1->right ||
+ lprcSrc1->top >= lprcSrc2->bottom || lprcSrc2->top >= lprcSrc1->bottom) {
+ setRectEmpty(lprcDst);
+ return false;
+ }
+ lprcDst->left = MAX(lprcSrc1->left, lprcSrc2->left);
+ lprcDst->right = MIN(lprcSrc1->right, lprcSrc2->right);
+ lprcDst->top = MAX(lprcSrc1->top, lprcSrc2->top);
+ lprcDst->bottom = MIN(lprcSrc1->bottom, lprcSrc2->bottom);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::unionRect(Rect32 *lprcDst, Rect32 *lprcSrc1, Rect32 *lprcSrc2) {
+ if (isRectEmpty(lprcSrc1)) {
+ if (isRectEmpty(lprcSrc2)) {
+ setRectEmpty(lprcDst);
+ return false;
+ } else {
+ *lprcDst = *lprcSrc2;
+ }
+ } else {
+ if (isRectEmpty(lprcSrc2)) {
+ *lprcDst = *lprcSrc1;
+ } else {
+ lprcDst->left = MIN(lprcSrc1->left, lprcSrc2->left);
+ lprcDst->top = MIN(lprcSrc1->top, lprcSrc2->top);
+ lprcDst->right = MAX(lprcSrc1->right, lprcSrc2->right);
+ lprcDst->bottom = MAX(lprcSrc1->bottom, lprcSrc2->bottom);
+ }
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BasePlatform::copyRect(Rect32 *lprcDst, Rect32 *lprcSrc) {
+ if (lprcDst == NULL || lprcSrc == NULL) {
+ return false;
+ }
+
+ *lprcDst = *lprcSrc;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString BasePlatform::getPlatformName() {
+ // TODO: Should conform to the WME-spec.
+ //return AnsiString(SDL_GetPlatform());
+ return AnsiString("ScummVM");
+}
+
+//////////////////////////////////////////////////////////////////////////
+char *BasePlatform::strlwr(char *string) {
+ if (string) {
+ for (size_t i = 0; i < strlen(string); ++i) {
+ string[i] = tolower(string[i]);
+ }
+ }
+ return string;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/platform_osystem.h b/engines/wintermute/platform_osystem.h
new file mode 100644
index 0000000000..21a77e0a0e
--- /dev/null
+++ b/engines/wintermute/platform_osystem.h
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_PLATFORMSDL_H
+#define WINTERMUTE_PLATFORMSDL_H
+
+#include "engines/wintermute/dctypes.h"
+#include "engines/wintermute/math/rect32.h"
+#include "common/events.h"
+
+namespace Wintermute {
+
+class BaseGame;
+
+//////////////////////////////////////////////////////////////////////////
+class BasePlatform {
+public:
+ static int initialize(BaseGame *inGame, int argc, char *argv[]);
+ static void handleEvent(Common::Event *event);
+ static AnsiString getPlatformName();
+
+ // Win32 API bindings
+ static bool getCursorPos(Point32 *lpPoint);
+ static bool setCursorPos(int x, int y);
+ static bool showWindow(int nCmdShow);
+
+ static void setCapture();
+ static bool releaseCapture();
+
+ static bool setRectEmpty(Rect32 *lprc);
+ static bool isRectEmpty(const Rect32 *lprc);
+ static bool ptInRect(Rect32 *lprc, Point32 p);
+ static bool setRect(Rect32 *lprc, int left, int top, int right, int bottom);
+ static bool intersectRect(Rect32 *lprcDst, const Rect32 *lprcSrc1, const Rect32 *lprcSrc2);
+ static bool unionRect(Rect32 *lprcDst, Rect32 *lprcSrc1, Rect32 *lprcSrc2);
+ static bool copyRect(Rect32 *lprcDst, Rect32 *lprcSrc);
+
+ // string functions
+ static char *strlwr(char *string);
+
+private:
+ // Set by initialize on game-startup, the object referred to is also deleted by deinit in WintermuteEngine
+ static BaseGame *_gameRef;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/system/sys_class.cpp b/engines/wintermute/system/sys_class.cpp
new file mode 100644
index 0000000000..06b36b84de
--- /dev/null
+++ b/engines/wintermute/system/sys_class.cpp
@@ -0,0 +1,220 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/system/sys_instance.h"
+#include "engines/wintermute/system/sys_class.h"
+#include "engines/wintermute/system/sys_class_registry.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_persistence_manager.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+SystemClass::SystemClass(const AnsiString &name, PERSISTBUILD build, PERSISTLOAD load, bool persistentClass) {
+ _name = name;
+
+ _build = build;
+ _load = load;
+ _next = NULL;
+ _savedID = -1;
+ _persistent = persistentClass;
+ _numInst = 0;
+
+ SystemClassRegistry::getInstance()->registerClass(this);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SystemClass::~SystemClass() {
+ SystemClassRegistry::getInstance()->unregisterClass(this);
+ removeAllInstances();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClass::removeAllInstances() {
+ Instances::iterator it;
+ for (it = _instances.begin(); it != _instances.end(); ++it) {
+ delete(it->_value);
+ }
+ _instances.clear();
+ _instanceMap.clear();
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+SystemInstance *SystemClass::addInstance(void *instance, int id, int savedId) {
+ SystemInstance *inst = new SystemInstance(instance, id, this);
+ inst->setSavedID(savedId);
+ _instances[inst] = (inst);
+
+ _instanceMap[instance] = inst;
+
+ SystemClassRegistry::getInstance()->addInstanceToTable(inst, instance);
+
+ return inst;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClass::removeInstance(void *instance) {
+ InstanceMap::iterator mapIt = _instanceMap.find(instance);
+ if (mapIt == _instanceMap.end()) {
+ return false;
+ }
+
+ Instances::iterator it = _instances.find((mapIt->_value));
+ if (it != _instances.end()) {
+ delete(it->_value);
+ _instances.erase(it);
+ }
+
+ _instanceMap.erase(mapIt);
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int SystemClass::getInstanceID(void *pointer) {
+ InstanceMap::iterator mapIt = _instanceMap.find(pointer);
+ if (mapIt == _instanceMap.end()) {
+ return -1;
+ } else {
+ return (mapIt->_value)->getID();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *SystemClass::idToPointer(int savedID) {
+ //slow
+ Instances::iterator it;
+ for (it = _instances.begin(); it != _instances.end(); ++it) {
+ if ((it->_value)->getSavedID() == savedID) {
+ return (it->_value)->getInstance();
+ }
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int SystemClass::getNumInstances() {
+ return _instances.size();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClass::dump(Common::WriteStream *stream) {
+ Common::String str;
+ str = Common::String::format("%03d %c %-20s instances: %d\n", _iD, _persistent ? 'p' : ' ', _name.c_str(), getNumInstances());
+ stream->write(str.c_str(), str.size());
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClass::saveTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) {
+ persistMgr->putString(_name.c_str());
+ persistMgr->putDWORD(_iD);
+ persistMgr->putDWORD(_instances.size());
+
+ Instances::iterator it;
+ for (it = _instances.begin(); it != _instances.end(); ++it) {
+ persistMgr->putDWORD((it->_value)->getID());
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClass::loadTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) {
+ _savedID = persistMgr->getDWORD();
+ int numInstances = persistMgr->getDWORD();
+
+ for (int i = 0; i < numInstances; i++) {
+ int instID = persistMgr->getDWORD();
+ if (_persistent) {
+
+ if (i > 0) {
+ gameRef->LOG(0, "Warning: attempting to load multiple instances of persistent class %s (%d)", _name.c_str(), numInstances);
+ continue;
+ }
+
+ Instances::iterator it = _instances.begin();
+ if (it != _instances.end()) {
+ (it->_value)->setSavedID(instID);
+ SystemClassRegistry::getInstance()->addInstanceToTable((it->_value), (it->_value)->getInstance());
+ } else {
+ gameRef->LOG(0, "Warning: instance %d of persistent class %s not found", i, _name.c_str());
+ }
+ }
+ // normal instances, create empty objects
+ else {
+ void *emptyObject = _build();
+ if (!emptyObject) {
+ warning("HALT");
+ }
+
+ addInstance(emptyObject, SystemClassRegistry::getInstance()->getNextID(), instID);
+ }
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClass::saveInstances(BaseGame *Game, BasePersistenceManager *persistMgr) {
+ Instances::iterator it;
+ for (it = _instances.begin(); it != _instances.end(); ++it) {
+ // write instace header
+ persistMgr->putString("<INSTANCE_HEAD>");
+ persistMgr->putDWORD(_iD);
+ persistMgr->putDWORD((it->_value)->getID());
+ persistMgr->putString("</INSTANCE_HEAD>");
+ _load((it->_value)->getInstance(), persistMgr);
+ persistMgr->putString("</INSTANCE>");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClass::loadInstance(void *instance, BasePersistenceManager *persistMgr) {
+ _load(instance, persistMgr);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClass::resetSavedIDs() {
+ Instances::iterator it;
+ for (it = _instances.begin(); it != _instances.end(); ++it) {
+ (it->_value)->setSavedID(-1);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClass::instanceCallback(SYS_INSTANCE_CALLBACK lpCallback, void *lpData) {
+ Instances::iterator it;
+ for (it = _instances.begin(); it != _instances.end(); ++it) {
+ lpCallback((it->_value)->getInstance(), lpData);
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/system/sys_class.h b/engines/wintermute/system/sys_class.h
new file mode 100644
index 0000000000..3f91723ed8
--- /dev/null
+++ b/engines/wintermute/system/sys_class.h
@@ -0,0 +1,130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SYSCLASS_H
+#define WINTERMUTE_SYSCLASS_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/dctypes.h"
+#include "common/hashmap.h"
+#include "common/func.h"
+#include "common/stream.h"
+
+namespace Wintermute {
+class SystemInstance;
+class BaseGame;
+class BasePersistenceManager;
+class SystemClass;
+
+}
+
+namespace Common {
+template<typename T> struct Hash;
+
+template<> struct Hash<void *> : public UnaryFunction<void *, uint> {
+ uint operator()(void *val) const {
+ return (uint)((size_t)val);
+ }
+};
+
+template<> struct Hash<Wintermute::SystemInstance *> : public UnaryFunction<Wintermute::SystemInstance *, uint> {
+ uint operator()(Wintermute::SystemInstance *val) const {
+ return (uint)((size_t)val);
+ }
+};
+
+
+}
+
+namespace Wintermute {
+
+class SystemClass {
+public:
+ SystemClass(const AnsiString &name, PERSISTBUILD build, PERSISTLOAD load, bool persistentClass);
+ ~SystemClass();
+
+ int getNumInstances();
+ bool removeInstance(void *instance);
+ SystemInstance *addInstance(void *instance, int id, int savedId = -1);
+ bool removeAllInstances();
+
+ int getInstanceID(void *pointer);
+ void *idToPointer(int savedID);
+
+ void setID(int id) {
+ _iD = id;
+ }
+ int getID() const {
+ return _iD;
+ }
+
+ int getSavedID() const {
+ return _savedID;
+ }
+
+ bool isPersistent() const {
+ return _persistent;
+ }
+
+ AnsiString getName() const {
+ return _name;
+ }
+
+ void saveTable(BaseGame *Game, BasePersistenceManager *PersistMgr);
+ void loadTable(BaseGame *Game, BasePersistenceManager *PersistMgr);
+
+ void saveInstances(BaseGame *Game, BasePersistenceManager *PersistMgr);
+ void loadInstance(void *instance, BasePersistenceManager *PersistMgr);
+
+ void instanceCallback(SYS_INSTANCE_CALLBACK lpCallback, void *lpData);
+
+ void resetSavedIDs();
+
+ void dump(Common::WriteStream *stream);
+
+private:
+ int _numInst;
+ bool _persistent;
+ SystemClass *_next;
+ int _iD;
+ int _savedID;
+ AnsiString _name;
+ PERSISTBUILD _build;
+ PERSISTLOAD _load;
+
+ //typedef std::set<SystemInstance *> Instances;
+ typedef Common::HashMap<SystemInstance *, SystemInstance *> Instances;
+ Instances _instances;
+
+ typedef Common::HashMap<void *, SystemInstance *> InstanceMap;
+ InstanceMap _instanceMap;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/system/sys_class_registry.cpp b/engines/wintermute/system/sys_class_registry.cpp
new file mode 100644
index 0000000000..7c1911c2bf
--- /dev/null
+++ b/engines/wintermute/system/sys_class_registry.cpp
@@ -0,0 +1,336 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/system/sys_instance.h"
+#include "engines/wintermute/system/sys_class_registry.h"
+#include "engines/wintermute/system/sys_class.h"
+#include "engines/wintermute/wintermute.h"
+#include "common/stream.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+SystemClassRegistry::SystemClassRegistry() {
+ _count = 0;
+ _disabled = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+SystemClassRegistry::~SystemClassRegistry() {
+ unregisterClasses();
+}
+
+//////////////////////////////////////////////////////////////////////////
+SystemClassRegistry *SystemClassRegistry::getInstance() {
+ return BaseEngine::instance().getClassRegistry();
+}
+
+void SystemClassRegistry::unregisterClasses() {
+ // SystemClass calls UnregisterClass upon destruction.
+ while (_classes.size() > 0) {
+ delete _classes.begin()->_value;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::registerClass(SystemClass *classObj) {
+ classObj->setID(_count++);
+ //_classes.insert(classObj);
+ _classes[classObj] = classObj;
+
+ _nameMap[classObj->getName()] = classObj;
+ _idMap[classObj->getID()] = classObj;
+
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::unregisterClass(SystemClass *classObj) {
+
+ Classes::iterator it = _classes.find(classObj);
+ if (it == _classes.end()) {
+ return false;
+ }
+
+ if (classObj->getNumInstances() != 0) {
+ debugC(Wintermute::kWintermuteDebugSaveGame, "Memory leak@class %-20s: %d instance(s) left\n", classObj->getName().c_str(), classObj->getNumInstances());
+ }
+ _classes.erase(it);
+
+ NameMap::iterator mapIt = _nameMap.find(classObj->getName());
+ if (mapIt != _nameMap.end()) {
+ _nameMap.erase(mapIt);
+ }
+
+ IdMap::iterator idIt = _idMap.find(classObj->getID());
+ if (idIt != _idMap.end()) {
+ _idMap.erase(idIt);
+ }
+
+
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::registerInstance(const char *className, void *instance) {
+ if (_disabled) {
+ return true;
+ }
+
+ NameMap::iterator mapIt = _nameMap.find(className);
+ if (mapIt == _nameMap.end()) {
+ return false;
+ }
+
+ SystemInstance *inst = (*mapIt)._value->addInstance(instance, _count++);
+ return (inst != NULL);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClassRegistry::addInstanceToTable(SystemInstance *instance, void *pointer) {
+ _instanceMap[pointer] = instance;
+
+ if (instance->getSavedID() >= 0) {
+ _savedInstanceMap[instance->getSavedID()] = instance;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+int SystemClassRegistry::getNextID() {
+ return _count++;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::unregisterInstance(const char *className, void *instance) {
+ NameMap::iterator mapIt = _nameMap.find(className);
+ if (mapIt == _nameMap.end()) {
+ return false;
+ }
+ (*mapIt)._value->removeInstance(instance);
+
+ InstanceMap::iterator instIt = _instanceMap.find(instance);
+ if (instIt != _instanceMap.end()) {
+ _instanceMap.erase(instIt);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::getPointerID(void *pointer, int *classID, int *instanceID) {
+ if (pointer == NULL) {
+ return true;
+ }
+
+ InstanceMap::iterator it = _instanceMap.find(pointer);
+ if (it == _instanceMap.end()) {
+ return false;
+ }
+
+
+ SystemInstance *inst = (*it)._value;
+ *instanceID = inst->getID();
+ *classID = inst->getClass()->getID();
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *SystemClassRegistry::idToPointer(int classID, int instanceID) {
+ SavedInstanceMap::iterator it = _savedInstanceMap.find(instanceID);
+ if (it == _savedInstanceMap.end()) {
+ return NULL;
+ } else {
+ return (*it)._value->getInstance();
+ }
+}
+
+bool checkHeader(const char *tag, BasePersistenceManager *pm) {
+ char *test = pm->getString();
+ Common::String verify = test;
+ delete[] test;
+ bool retVal = (verify == tag);
+ if (!retVal) {
+ error("Expected %s in Save-file not found", tag);
+ }
+ return retVal;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::saveTable(BaseGame *gameRef, BasePersistenceManager *persistMgr, bool quickSave) {
+ persistMgr->putString("<CLASS_REGISTRY_TABLE>");
+ persistMgr->putDWORD(_classes.size());
+
+ int counter = 0;
+
+ Classes::iterator it;
+ for (it = _classes.begin(); it != _classes.end(); ++it) {
+ counter++;
+
+ if (!quickSave) {
+ gameRef->_renderer->setIndicatorVal((int)(50.0f / (float)((float)_classes.size() / (float)counter)));
+ }
+
+ (it->_value)->saveTable(gameRef, persistMgr);
+ }
+ persistMgr->putString("</CLASS_REGISTRY_TABLE>");
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::loadTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) {
+ checkHeader("<CLASS_REGISTRY_TABLE>", persistMgr);
+
+ // reset SavedID of current instances
+ Classes::iterator it;
+ for (it = _classes.begin(); it != _classes.end(); ++it) {
+ (it->_value)->resetSavedIDs();
+ }
+
+ for (it = _classes.begin(); it != _classes.end(); ++it) {
+ if ((it->_value)->isPersistent()) {
+ continue;
+ }
+ (it->_value)->removeAllInstances();
+ }
+
+ _instanceMap.clear();
+
+ uint32 numClasses = persistMgr->getDWORD();
+
+ for (uint32 i = 0; i < numClasses; i++) {
+ gameRef->_renderer->setIndicatorVal((int)(50.0f / (float)((float)numClasses / (float)i)));
+
+ Common::String className = persistMgr->getStringObj();
+ NameMap::iterator mapIt = _nameMap.find(className);
+ if (mapIt != _nameMap.end()) {
+ (*mapIt)._value->loadTable(gameRef, persistMgr);
+ }
+ }
+
+ checkHeader("</CLASS_REGISTRY_TABLE>", persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::saveInstances(BaseGame *gameRef, BasePersistenceManager *persistMgr, bool quickSave) {
+
+ Classes::iterator it;
+
+ // count total instances
+ int numInstances = 0;
+ for (it = _classes.begin(); it != _classes.end(); ++it) {
+ numInstances += (it->_value)->getNumInstances();
+ }
+
+ persistMgr->putDWORD(numInstances);
+
+ int counter = 0;
+ for (it = _classes.begin(); it != _classes.end(); ++it) {
+ counter++;
+
+ if (!quickSave) {
+ if (counter % 20 == 0) {
+ gameRef->_renderer->setIndicatorVal((int)(50.0f + 50.0f / (float)((float)_classes.size() / (float)counter)));
+ }
+ }
+ gameRef->miniUpdate();
+
+ (it->_value)->saveInstances(gameRef, persistMgr);
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::loadInstances(BaseGame *gameRef, BasePersistenceManager *persistMgr) {
+ // get total instances
+ int numInstances = persistMgr->getDWORD();
+
+ for (int i = 0; i < numInstances; i++) {
+ if (i % 20 == 0) {
+ gameRef->_renderer->setIndicatorVal((int)(50.0f + 50.0f / (float)((float)numInstances / (float)i)));
+ }
+
+ checkHeader("<INSTANCE_HEAD>", persistMgr);
+
+ int classID = persistMgr->getDWORD();
+ int instanceID = persistMgr->getDWORD();
+ void *instance = idToPointer(classID, instanceID);
+
+ checkHeader("</INSTANCE_HEAD>", persistMgr);
+
+ Classes::iterator it;
+ for (it = _classes.begin(); it != _classes.end(); ++it) {
+ if ((it->_value)->getSavedID() == classID) {
+ (it->_value)->loadInstance(instance, persistMgr);
+ break;
+ }
+ }
+ checkHeader("</INSTANCE>", persistMgr);
+ }
+
+ _savedInstanceMap.clear();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool SystemClassRegistry::enumInstances(SYS_INSTANCE_CALLBACK lpCallback, const char *className, void *lpData) {
+ NameMap::iterator mapIt = _nameMap.find(className);
+ if (mapIt == _nameMap.end()) {
+ return STATUS_FAILED;
+ }
+
+ (*mapIt)._value->instanceCallback(lpCallback, lpData);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void SystemClassRegistry::dumpClasses(Common::WriteStream *stream) {
+ Classes::iterator it;
+ for (it = _classes.begin(); it != _classes.end(); ++it) {
+ (it->_value)->dump(stream);
+ }
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/system/sys_class_registry.h b/engines/wintermute/system/sys_class_registry.h
new file mode 100644
index 0000000000..ef7218c7c1
--- /dev/null
+++ b/engines/wintermute/system/sys_class_registry.h
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SYSCLASSREGISTRY_H
+#define WINTERMUTE_SYSCLASSREGISTRY_H
+
+#include "engines/wintermute/wintypes.h"
+#include "engines/wintermute/dctypes.h"
+#include "engines/wintermute/system/sys_class.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/func.h"
+#include "common/stream.h"
+
+namespace Wintermute {
+class SystemClass;
+}
+
+namespace Common {
+template<typename T> struct Hash;
+template<> struct Hash<Wintermute::SystemClass *> : public UnaryFunction<Wintermute::SystemClass *, uint> {
+ uint operator()(Wintermute::SystemClass *val) const {
+ return (uint)((size_t)val);
+ }
+};
+
+}
+
+namespace Wintermute {
+
+class BaseGame;
+class BasePersistenceManager;
+class SystemInstance;
+
+class SystemClassRegistry {
+ void unregisterClasses();
+public:
+ void registerClasses(); // persistent.cpp
+ static SystemClassRegistry *getInstance();
+
+ SystemClassRegistry();
+ virtual ~SystemClassRegistry();
+
+ bool enumInstances(SYS_INSTANCE_CALLBACK lpCallback, const char *className, void *lpData);
+ bool loadTable(BaseGame *Game, BasePersistenceManager *PersistMgr);
+ bool saveTable(BaseGame *Game, BasePersistenceManager *PersistMgr, bool quickSave);
+ bool loadInstances(BaseGame *Game, BasePersistenceManager *PersistMgr);
+ bool saveInstances(BaseGame *Game, BasePersistenceManager *PersistMgr, bool quickSave);
+ void *idToPointer(int classID, int instanceID);
+ bool getPointerID(void *pointer, int *classID, int *instanceID);
+ bool registerClass(SystemClass *classObj);
+ bool unregisterClass(SystemClass *classObj);
+ bool registerInstance(const char *className, void *instance);
+ bool unregisterInstance(const char *className, void *instance);
+ void dumpClasses(Common::WriteStream *stream);
+ int getNextID();
+ void addInstanceToTable(SystemInstance *instance, void *pointer);
+
+ bool _disabled;
+ int _count;
+
+ typedef Common::HashMap<SystemClass *, SystemClass *> Classes;
+ Classes _classes;
+
+ typedef Common::HashMap<AnsiString, SystemClass *> NameMap;
+ NameMap _nameMap;
+
+ typedef Common::HashMap<int, SystemClass *> IdMap;
+ IdMap _idMap;
+
+ typedef Common::HashMap<void *, SystemInstance *> InstanceMap;
+ InstanceMap _instanceMap;
+
+ typedef Common::HashMap<int, SystemInstance *> SavedInstanceMap;
+ SavedInstanceMap _savedInstanceMap;
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/system/sys_instance.cpp b/engines/wintermute/system/sys_instance.cpp
new file mode 100644
index 0000000000..d106119dba
--- /dev/null
+++ b/engines/wintermute/system/sys_instance.cpp
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/system/sys_instance.h"
+#include "engines/wintermute/system/sys_class_registry.h"
+#include "engines/wintermute/system/sys_class.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+SystemInstance::SystemInstance(void *instance, int id, SystemClass *sysClass) {
+ _instance = instance;
+ _id = id;
+ _savedID = -1;
+ _class = sysClass;
+
+ _used = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+SystemInstance::~SystemInstance() {
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/system/sys_instance.h b/engines/wintermute/system/sys_instance.h
new file mode 100644
index 0000000000..215a6d1437
--- /dev/null
+++ b/engines/wintermute/system/sys_instance.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SYSINSTANCE_H
+#define WINTERMUTE_SYSINSTANCE_H
+
+namespace Wintermute {
+
+class SystemClass;
+
+class SystemInstance {
+public:
+ SystemInstance(void *instance, int id, SystemClass *sysClass);
+ virtual ~SystemInstance();
+
+ int getID() const {
+ return _id;
+ }
+ int getSavedID() const {
+ return _savedID;
+ }
+ void *getInstance() const {
+ return _instance;
+ }
+ SystemClass *getClass() const {
+ return _class;
+ }
+
+ void setSavedID(int id) {
+ _savedID = id;
+ }
+
+private:
+ bool _used;
+ int _id;
+ int _savedID;
+ void *_instance;
+ SystemClass *_class;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ui/ui_button.cpp b/engines/wintermute/ui/ui_button.cpp
new file mode 100644
index 0000000000..7967d566f9
--- /dev/null
+++ b/engines/wintermute/ui/ui_button.cpp
@@ -0,0 +1,1209 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/ui/ui_button.h"
+#include "engines/wintermute/ui/ui_tiled_image.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_active_rect.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(UIButton, false)
+
+//////////////////////////////////////////////////////////////////////////
+UIButton::UIButton(BaseGame *inGame) : UIObject(inGame) {
+ _backPress = _backHover = _backDisable = _backFocus = NULL;
+
+ _fontHover = _fontPress = _fontDisable = _fontFocus = NULL;
+
+ _imageDisable = _imagePress = _imageHover = _imageFocus = NULL;
+
+ _align = TAL_CENTER;
+
+ _hover = _press = false;
+
+ _type = UI_BUTTON;
+
+ _canFocus = false;
+ _stayPressed = false;
+
+ _oneTimePress = false;
+ _centerImage = false;
+
+ _pixelPerfect = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+UIButton::~UIButton() {
+ delete _backPress;
+ delete _backHover;
+ delete _backDisable;
+ delete _backFocus;
+
+ if (!_sharedFonts) {
+ if (_fontHover) {
+ _gameRef->_fontStorage->removeFont(_fontHover);
+ }
+ if (_fontPress) {
+ _gameRef->_fontStorage->removeFont(_fontPress);
+ }
+ if (_fontDisable) {
+ _gameRef->_fontStorage->removeFont(_fontDisable);
+ }
+ if (_fontFocus) {
+ _gameRef->_fontStorage->removeFont(_fontFocus);
+ }
+ }
+
+ if (!_sharedImages) {
+ delete _imageHover;
+ delete _imagePress;
+ delete _imageDisable;
+ delete _imageFocus;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIButton::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "UIButton::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing BUTTON file '%s'", filename);
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(BUTTON)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(DISABLED)
+TOKEN_DEF(VISIBLE)
+TOKEN_DEF(FOCUSABLE)
+TOKEN_DEF(BACK_HOVER)
+TOKEN_DEF(BACK_PRESS)
+TOKEN_DEF(BACK_DISABLE)
+TOKEN_DEF(BACK_FOCUS)
+TOKEN_DEF(BACK)
+TOKEN_DEF(CENTER_IMAGE)
+TOKEN_DEF(IMAGE_HOVER)
+TOKEN_DEF(IMAGE_PRESS)
+TOKEN_DEF(IMAGE_DISABLE)
+TOKEN_DEF(IMAGE_FOCUS)
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(FONT_HOVER)
+TOKEN_DEF(FONT_PRESS)
+TOKEN_DEF(FONT_DISABLE)
+TOKEN_DEF(FONT_FOCUS)
+TOKEN_DEF(FONT)
+TOKEN_DEF(TEXT_ALIGN)
+TOKEN_DEF(TEXT)
+TOKEN_DEF(X)
+TOKEN_DEF(Y)
+TOKEN_DEF(WIDTH)
+TOKEN_DEF(HEIGHT)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(NAME)
+TOKEN_DEF(EVENTS)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PARENT_NOTIFY)
+TOKEN_DEF(PRESSED)
+TOKEN_DEF(PIXEL_PERFECT)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool UIButton::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(BUTTON)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(DISABLED)
+ TOKEN_TABLE(VISIBLE)
+ TOKEN_TABLE(FOCUSABLE)
+ TOKEN_TABLE(BACK_HOVER)
+ TOKEN_TABLE(BACK_PRESS)
+ TOKEN_TABLE(BACK_DISABLE)
+ TOKEN_TABLE(BACK_FOCUS)
+ TOKEN_TABLE(BACK)
+ TOKEN_TABLE(CENTER_IMAGE)
+ TOKEN_TABLE(IMAGE_HOVER)
+ TOKEN_TABLE(IMAGE_PRESS)
+ TOKEN_TABLE(IMAGE_DISABLE)
+ TOKEN_TABLE(IMAGE_FOCUS)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(FONT_HOVER)
+ TOKEN_TABLE(FONT_PRESS)
+ TOKEN_TABLE(FONT_DISABLE)
+ TOKEN_TABLE(FONT_FOCUS)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(TEXT_ALIGN)
+ TOKEN_TABLE(TEXT)
+ TOKEN_TABLE(X)
+ TOKEN_TABLE(Y)
+ TOKEN_TABLE(WIDTH)
+ TOKEN_TABLE(HEIGHT)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(EVENTS)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PARENT_NOTIFY)
+ TOKEN_TABLE(PRESSED)
+ TOKEN_TABLE(PIXEL_PERFECT)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd = 2;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_BUTTON) {
+ _gameRef->LOG(0, "'BUTTON' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_BACK:
+ delete _back;
+ _back = new UITiledImage(_gameRef);
+ if (!_back || DID_FAIL(_back->loadFile((char *)params))) {
+ delete _back;
+ _back = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_BACK_HOVER:
+ delete _backHover;
+ _backHover = new UITiledImage(_gameRef);
+ if (!_backHover || DID_FAIL(_backHover->loadFile((char *)params))) {
+ delete _backHover;
+ _backHover = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_BACK_PRESS:
+ delete _backPress;
+ _backPress = new UITiledImage(_gameRef);
+ if (!_backPress || DID_FAIL(_backPress->loadFile((char *)params))) {
+ delete _backPress;
+ _backPress = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_BACK_DISABLE:
+ delete _backDisable;
+ _backDisable = new UITiledImage(_gameRef);
+ if (!_backDisable || DID_FAIL(_backDisable->loadFile((char *)params))) {
+ delete _backDisable;
+ _backDisable = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_BACK_FOCUS:
+ delete _backFocus;
+ _backFocus = new UITiledImage(_gameRef);
+ if (!_backFocus || DID_FAIL(_backFocus->loadFile((char *)params))) {
+ delete _backFocus;
+ _backFocus = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE:
+ delete _image;
+ _image = new BaseSprite(_gameRef);
+ if (!_image || DID_FAIL(_image->loadFile((char *)params))) {
+ delete _image;
+ _image = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE_HOVER:
+ delete _imageHover;
+ _imageHover = new BaseSprite(_gameRef);
+ if (!_imageHover || DID_FAIL(_imageHover->loadFile((char *)params))) {
+ delete _imageHover;
+ _imageHover = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE_PRESS:
+ delete _imagePress;
+ _imagePress = new BaseSprite(_gameRef);
+ if (!_imagePress || DID_FAIL(_imagePress->loadFile((char *)params))) {
+ delete _imagePress;
+ _imagePress = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE_DISABLE:
+ delete _imageDisable;
+ _imageDisable = new BaseSprite(_gameRef);
+ if (!_imageDisable || DID_FAIL(_imageDisable->loadFile((char *)params))) {
+ delete _imageDisable;
+ _imageDisable = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE_FOCUS:
+ delete _imageFocus;
+ _imageFocus = new BaseSprite(_gameRef);
+ if (!_imageFocus || DID_FAIL(_imageFocus->loadFile((char *)params))) {
+ delete _imageFocus;
+ _imageFocus = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT:
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ _font = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_font) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT_HOVER:
+ if (_fontHover) {
+ _gameRef->_fontStorage->removeFont(_fontHover);
+ }
+ _fontHover = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_fontHover) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT_PRESS:
+ if (_fontPress) {
+ _gameRef->_fontStorage->removeFont(_fontPress);
+ }
+ _fontPress = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_fontPress) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT_DISABLE:
+ if (_fontDisable) {
+ _gameRef->_fontStorage->removeFont(_fontDisable);
+ }
+ _fontDisable = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_fontDisable) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT_FOCUS:
+ if (_fontFocus) {
+ _gameRef->_fontStorage->removeFont(_fontFocus);
+ }
+ _fontFocus = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_fontFocus) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_TEXT:
+ setText((char *)params);
+ _gameRef->_stringTable->expand(&_text);
+ break;
+
+ case TOKEN_TEXT_ALIGN:
+ if (scumm_stricmp((char *)params, "left") == 0) {
+ _align = TAL_LEFT;
+ } else if (scumm_stricmp((char *)params, "right") == 0) {
+ _align = TAL_RIGHT;
+ } else {
+ _align = TAL_CENTER;
+ }
+ break;
+
+ case TOKEN_X:
+ parser.scanStr((char *)params, "%d", &_posX);
+ break;
+
+ case TOKEN_Y:
+ parser.scanStr((char *)params, "%d", &_posY);
+ break;
+
+ case TOKEN_WIDTH:
+ parser.scanStr((char *)params, "%d", &_width);
+ break;
+
+ case TOKEN_HEIGHT:
+ parser.scanStr((char *)params, "%d", &_height);
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PARENT_NOTIFY:
+ parser.scanStr((char *)params, "%b", &_parentNotify);
+ break;
+
+ case TOKEN_DISABLED:
+ parser.scanStr((char *)params, "%b", &_disable);
+ break;
+
+ case TOKEN_VISIBLE:
+ parser.scanStr((char *)params, "%b", &_visible);
+ break;
+
+ case TOKEN_FOCUSABLE:
+ parser.scanStr((char *)params, "%b", &_canFocus);
+ break;
+
+ case TOKEN_CENTER_IMAGE:
+ parser.scanStr((char *)params, "%b", &_centerImage);
+ break;
+
+ case TOKEN_PRESSED:
+ parser.scanStr((char *)params, "%b", &_stayPressed);
+ break;
+
+ case TOKEN_PIXEL_PERFECT:
+ parser.scanStr((char *)params, "%b", &_pixelPerfect);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in BUTTON definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading BUTTON definition");
+ return STATUS_FAILED;
+ }
+
+ correctSize();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIButton::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "BUTTON\n");
+ buffer->putTextIndent(indent, "{\n");
+
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ if (_back && _back->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename());
+ }
+ if (_backHover && _backHover->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK_HOVER=\"%s\"\n", _backHover->getFilename());
+ }
+ if (_backPress && _backPress->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK_PRESS=\"%s\"\n", _backPress->getFilename());
+ }
+ if (_backDisable && _backDisable->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK_DISABLE=\"%s\"\n", _backDisable->getFilename());
+ }
+ if (_backFocus && _backFocus->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK_FOCUS=\"%s\"\n", _backFocus->getFilename());
+ }
+
+ if (_image && _image->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename());
+ }
+ if (_imageHover && _imageHover->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE_HOVER=\"%s\"\n", _imageHover->getFilename());
+ }
+ if (_imagePress && _imagePress->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE_PRESS=\"%s\"\n", _imagePress->getFilename());
+ }
+ if (_imageDisable && _imageDisable->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE_DISABLE=\"%s\"\n", _imageDisable->getFilename());
+ }
+ if (_imageFocus && _imageFocus->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE_FOCUS=\"%s\"\n", _imageFocus->getFilename());
+ }
+
+ if (_font && _font->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename());
+ }
+ if (_fontHover && _fontHover->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT_HOVER=\"%s\"\n", _fontHover->getFilename());
+ }
+ if (_fontPress && _fontPress->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT_PRESS=\"%s\"\n", _fontPress->getFilename());
+ }
+ if (_fontDisable && _fontDisable->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT_DISABLE=\"%s\"\n", _fontDisable->getFilename());
+ }
+ if (_fontFocus && _fontFocus->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT_FOCUS=\"%s\"\n", _fontFocus->getFilename());
+ }
+
+ if (_cursor && _cursor->getFilename()) {
+ buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename());
+ }
+
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ if (_text) {
+ buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text);
+ }
+
+ switch (_align) {
+ case TAL_LEFT:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left");
+ break;
+ case TAL_RIGHT:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right");
+ break;
+ case TAL_CENTER:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center");
+ break;
+ default:
+ warning("UIButton::SaveAsText - unhandled enum");
+ break;
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ buffer->putTextIndent(indent + 2, "X=%d\n", _posX);
+ buffer->putTextIndent(indent + 2, "Y=%d\n", _posY);
+ buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width);
+ buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height);
+
+
+ buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "FOCUSABLE=%s\n", _canFocus ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "CENTER_IMAGE=%s\n", _centerImage ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "PRESSED=%s\n", _stayPressed ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "PIXEL_PERFECT=%s\n", _pixelPerfect ? "TRUE" : "FALSE");
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // editor properties
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void UIButton::correctSize() {
+ Rect32 rect;
+
+ BaseSprite *img = NULL;
+ if (_image) {
+ img = _image;
+ } else if (_imageDisable) {
+ img = _imageDisable;
+ } else if (_imageHover) {
+ img = _imageHover;
+ } else if (_imagePress) {
+ img = _imagePress;
+ } else if (_imageFocus) {
+ img = _imageFocus;
+ }
+
+ if (_width <= 0) {
+ if (img) {
+ img->getBoundingRect(&rect, 0, 0);
+ _width = rect.right - rect.left;
+ } else {
+ _width = 100;
+ }
+ }
+
+ if (_height <= 0) {
+ if (img) {
+ img->getBoundingRect(&rect, 0, 0);
+ _height = rect.bottom - rect.top;
+ }
+ }
+
+ if (_text) {
+ int text_height;
+ if (_font) {
+ text_height = _font->getTextHeight((byte *)_text, _width);
+ } else {
+ text_height = _gameRef->_systemFont->getTextHeight((byte *)_text, _width);
+ }
+
+ if (text_height > _height) {
+ _height = text_height;
+ }
+ }
+
+ if (_height <= 0) {
+ _height = 100;
+ }
+
+ if (_back) {
+ _back->correctSize(&_width, &_height);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIButton::display(int offsetX, int offsetY) {
+ if (!_visible) {
+ return STATUS_OK;
+ }
+
+ UITiledImage *back = NULL;
+ BaseSprite *image = NULL;
+ BaseFont *font = 0;
+
+ //RECT rect;
+ //BasePlatform::setRect(&rect, OffsetX + _posX, OffsetY + _posY, OffsetX+_posX+_width, OffsetY+_posY+_height);
+ //_hover = (!_disable && BasePlatform::ptInRect(&rect, _gameRef->_mousePos)!=FALSE);
+ _hover = (!_disable && _gameRef->_activeObject == this && (_gameRef->_interactive || _gameRef->_state == GAME_SEMI_FROZEN));
+
+ if ((_press && _hover && !_gameRef->_mouseLeftDown) ||
+ (_oneTimePress && g_system->getMillis() - _oneTimePressTime >= 100)) {
+ press();
+ }
+
+
+ if (_disable) {
+ if (_backDisable) {
+ back = _backDisable;
+ }
+ if (_imageDisable) {
+ image = _imageDisable;
+ }
+ if (_text && _fontDisable) {
+ font = _fontDisable;
+ }
+ } else if (_press || _oneTimePress || _stayPressed) {
+ if (_backPress) {
+ back = _backPress;
+ }
+ if (_imagePress) {
+ image = _imagePress;
+ }
+ if (_text && _fontPress) {
+ font = _fontPress;
+ }
+ } else if (_hover) {
+ if (_backHover) {
+ back = _backHover;
+ }
+ if (_imageHover) {
+ image = _imageHover;
+ }
+ if (_text && _fontHover) {
+ font = _fontHover;
+ }
+ } else if (_canFocus && isFocused()) {
+ if (_backFocus) {
+ back = _backFocus;
+ }
+ if (_imageFocus) {
+ image = _imageFocus;
+ }
+ if (_text && _fontFocus) {
+ font = _fontFocus;
+ }
+ }
+
+ if (!back && _back) {
+ back = _back;
+ }
+ if (!image && _image) {
+ image = _image;
+ }
+ if (_text && !font) {
+ if (_font) {
+ font = _font;
+ } else {
+ font = _gameRef->_systemFont;
+ }
+ }
+
+ int imageX = offsetX + _posX;
+ int imageY = offsetY + _posY;
+
+ if (image && _centerImage) {
+ Rect32 rc;
+ image->getBoundingRect(&rc, 0, 0);
+ imageX += (_width - (rc.right - rc.left)) / 2;
+ imageY += (_height - (rc.bottom - rc.top)) / 2;
+ }
+
+ if (back) {
+ back->display(offsetX + _posX, offsetY + _posY, _width, _height);
+ }
+ //if (image) image->Draw(ImageX +((_press||_oneTimePress)&&back?1:0), ImageY +((_press||_oneTimePress)&&back?1:0), NULL);
+ if (image) {
+ image->draw(imageX + ((_press || _oneTimePress) && back ? 1 : 0), imageY + ((_press || _oneTimePress) && back ? 1 : 0), _pixelPerfect ? this : NULL);
+ }
+
+ if (font && _text) {
+ int text_offset = (_height - font->getTextHeight((byte *)_text, _width)) / 2;
+ font->drawText((byte *)_text, offsetX + _posX + ((_press || _oneTimePress) ? 1 : 0), offsetY + _posY + text_offset + ((_press || _oneTimePress) ? 1 : 0), _width, _align);
+ }
+
+ if (!_pixelPerfect || !_image) {
+ _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, offsetX + _posX, offsetY + _posY, _width, _height, 100, 100, false));
+ }
+
+ // reset unused sprites
+ if (_image && _image != image) {
+ _image->reset();
+ }
+ if (_imageDisable && _imageDisable != image) {
+ _imageDisable->reset();
+ }
+ if (_imageFocus && _imageFocus != image) {
+ _imageFocus->reset();
+ }
+ if (_imagePress && _imagePress != image) {
+ _imagePress->reset();
+ }
+ if (_imageHover && _imageHover != image) {
+ _imageHover->reset();
+ }
+
+ _press = _hover && _gameRef->_mouseLeftDown && _gameRef->_capturedObject == this;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void UIButton::press() {
+ applyEvent("Press");
+ if (_listenerObject) {
+ _listenerObject->listen(_listenerParamObject, _listenerParamDWORD);
+ }
+ if (_parentNotify && _parent) {
+ _parent->applyEvent(getName());
+ }
+
+ _oneTimePress = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool UIButton::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetDisabledFont
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetDisabledFont") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ if (_fontDisable) {
+ _gameRef->_fontStorage->removeFont(_fontDisable);
+ }
+ if (val->isNULL()) {
+ _fontDisable = NULL;
+ stack->pushBool(true);
+ } else {
+ _fontDisable = _gameRef->_fontStorage->addFont(val->getString());
+ stack->pushBool(_fontDisable != NULL);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetHoverFont
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetHoverFont") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ if (_fontHover) {
+ _gameRef->_fontStorage->removeFont(_fontHover);
+ }
+ if (val->isNULL()) {
+ _fontHover = NULL;
+ stack->pushBool(true);
+ } else {
+ _fontHover = _gameRef->_fontStorage->addFont(val->getString());
+ stack->pushBool(_fontHover != NULL);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetPressedFont
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetPressedFont") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ if (_fontPress) {
+ _gameRef->_fontStorage->removeFont(_fontPress);
+ }
+ if (val->isNULL()) {
+ _fontPress = NULL;
+ stack->pushBool(true);
+ } else {
+ _fontPress = _gameRef->_fontStorage->addFont(val->getString());
+ stack->pushBool(_fontPress != NULL);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetFocusedFont
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetFocusedFont") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ if (_fontFocus) {
+ _gameRef->_fontStorage->removeFont(_fontFocus);
+ }
+ if (val->isNULL()) {
+ _fontFocus = NULL;
+ stack->pushBool(true);
+ } else {
+ _fontFocus = _gameRef->_fontStorage->addFont(val->getString());
+ stack->pushBool(_fontFocus != NULL);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetDisabledImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetDisabledImage") == 0) {
+ stack->correctParams(1);
+
+ delete _imageDisable;
+ _imageDisable = new BaseSprite(_gameRef);
+ const char *filename = stack->pop()->getString();
+ if (!_imageDisable || DID_FAIL(_imageDisable->loadFile(filename))) {
+ delete _imageDisable;
+ _imageDisable = NULL;
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetDisabledImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetDisabledImage") == 0) {
+ stack->correctParams(0);
+ if (!_imageDisable || !_imageDisable->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_imageDisable->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetDisabledImageObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetDisabledImageObject") == 0) {
+ stack->correctParams(0);
+ if (!_imageDisable) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_imageDisable, true);
+ }
+
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetHoverImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetHoverImage") == 0) {
+ stack->correctParams(1);
+
+ delete _imageHover;
+ _imageHover = new BaseSprite(_gameRef);
+ const char *filename = stack->pop()->getString();
+ if (!_imageHover || DID_FAIL(_imageHover->loadFile(filename))) {
+ delete _imageHover;
+ _imageHover = NULL;
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetHoverImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHoverImage") == 0) {
+ stack->correctParams(0);
+ if (!_imageHover || !_imageHover->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_imageHover->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetHoverImageObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHoverImageObject") == 0) {
+ stack->correctParams(0);
+ if (!_imageHover) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_imageHover, true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetPressedImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetPressedImage") == 0) {
+ stack->correctParams(1);
+
+ delete _imagePress;
+ _imagePress = new BaseSprite(_gameRef);
+ const char *filename = stack->pop()->getString();
+ if (!_imagePress || DID_FAIL(_imagePress->loadFile(filename))) {
+ delete _imagePress;
+ _imagePress = NULL;
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetPressedImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetPressedImage") == 0) {
+ stack->correctParams(0);
+ if (!_imagePress || !_imagePress->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_imagePress->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetPressedImageObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetPressedImageObject") == 0) {
+ stack->correctParams(0);
+ if (!_imagePress) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_imagePress, true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetFocusedImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetFocusedImage") == 0) {
+ stack->correctParams(1);
+
+ delete _imageFocus;
+ _imageFocus = new BaseSprite(_gameRef);
+ const char *filename = stack->pop()->getString();
+ if (!_imageFocus || DID_FAIL(_imageFocus->loadFile(filename))) {
+ delete _imageFocus;
+ _imageFocus = NULL;
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFocusedImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFocusedImage") == 0) {
+ stack->correctParams(0);
+ if (!_imageFocus || !_imageFocus->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_imageFocus->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFocusedImageObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFocusedImageObject") == 0) {
+ stack->correctParams(0);
+ if (!_imageFocus) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_imageFocus, true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Press
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Press") == 0) {
+ stack->correctParams(0);
+
+ if (_visible && !_disable) {
+ _oneTimePress = true;
+ _oneTimePressTime = g_system->getMillis();
+ }
+ stack->pushNULL();
+
+ return STATUS_OK;
+ } else {
+ return UIObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *UIButton::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("button");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TextAlign
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TextAlign") {
+ _scValue->setInt(_align);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Focusable
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Focusable") {
+ _scValue->setBool(_canFocus);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Pressed
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Pressed") {
+ _scValue->setBool(_stayPressed);
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // PixelPerfect
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "PixelPerfect") {
+ _scValue->setBool(_pixelPerfect);
+ return _scValue;
+ } else {
+ return UIObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIButton::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // TextAlign
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "TextAlign") == 0) {
+ int i = value->getInt();
+ if (i < 0 || i >= NUM_TEXT_ALIGN) {
+ i = 0;
+ }
+ _align = (TTextAlign)i;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Focusable
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Focusable") == 0) {
+ _canFocus = value->getBool();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Pressed
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Pressed") == 0) {
+ _stayPressed = value->getBool();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // PixelPerfect
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PixelPerfect") == 0) {
+ _pixelPerfect = value->getBool();
+ return STATUS_OK;
+ } else {
+ return UIObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *UIButton::scToString() {
+ return "[button]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIButton::persist(BasePersistenceManager *persistMgr) {
+
+ UIObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER_INT(_align));
+ persistMgr->transfer(TMEMBER(_backDisable));
+ persistMgr->transfer(TMEMBER(_backFocus));
+ persistMgr->transfer(TMEMBER(_backHover));
+ persistMgr->transfer(TMEMBER(_backPress));
+ persistMgr->transfer(TMEMBER(_centerImage));
+ persistMgr->transfer(TMEMBER(_fontDisable));
+ persistMgr->transfer(TMEMBER(_fontFocus));
+ persistMgr->transfer(TMEMBER(_fontHover));
+ persistMgr->transfer(TMEMBER(_fontPress));
+ persistMgr->transfer(TMEMBER(_hover));
+ persistMgr->transfer(TMEMBER(_image));
+ persistMgr->transfer(TMEMBER(_imageDisable));
+ persistMgr->transfer(TMEMBER(_imageFocus));
+ persistMgr->transfer(TMEMBER(_imageHover));
+ persistMgr->transfer(TMEMBER(_imagePress));
+ persistMgr->transfer(TMEMBER(_pixelPerfect));
+ persistMgr->transfer(TMEMBER(_press));
+ persistMgr->transfer(TMEMBER(_stayPressed));
+
+ if (!persistMgr->getIsSaving()) {
+ _oneTimePress = false;
+ _oneTimePressTime = 0;
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ui/ui_button.h b/engines/wintermute/ui/ui_button.h
new file mode 100644
index 0000000000..93333a2534
--- /dev/null
+++ b/engines/wintermute/ui/ui_button.h
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_UIBUTTON_H
+#define WINTERMUTE_UIBUTTON_H
+
+
+#include "engines/wintermute/ui/ui_object.h"
+#include "engines/wintermute/dctypes.h" // Added by ClassView
+
+namespace Wintermute {
+
+class UIButton : public UIObject {
+public:
+ bool _pixelPerfect;
+ bool _stayPressed;
+ bool _centerImage;
+ bool _oneTimePress;
+ uint32 _oneTimePressTime;
+ DECLARE_PERSISTENT(UIButton, UIObject)
+ void press();
+ virtual bool display() { return display(0, 0); }
+ virtual bool display(int offsetX, int offsetY);
+ bool _press;
+ bool _hover;
+ void correctSize();
+ TTextAlign _align;
+ BaseSprite *_imageHover;
+ BaseSprite *_imagePress;
+ BaseSprite *_imageDisable;
+ BaseSprite *_imageFocus;
+ BaseFont *_fontDisable;
+ BaseFont *_fontPress;
+ BaseFont *_fontHover;
+ BaseFont *_fontFocus;
+ UITiledImage *_backPress;
+ UITiledImage *_backHover;
+ UITiledImage *_backDisable;
+ UITiledImage *_backFocus;
+ UIButton(BaseGame *inGame = NULL);
+ virtual ~UIButton();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ui/ui_edit.cpp b/engines/wintermute/ui/ui_edit.cpp
new file mode 100644
index 0000000000..a3283d5a01
--- /dev/null
+++ b/engines/wintermute/ui/ui_edit.cpp
@@ -0,0 +1,952 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ui/ui_edit.h"
+#include "engines/wintermute/ui/ui_object.h"
+#include "engines/wintermute/ui/ui_tiled_image.h"
+#include "engines/wintermute/utils/string_util.h"
+#include "engines/wintermute/base/base_active_rect.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/base_keyboard_state.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/utils/utils.h"
+#include "common/util.h"
+#include "common/keyboard.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(UIEdit, false)
+
+//////////////////////////////////////////////////////////////////////////
+UIEdit::UIEdit(BaseGame *inGame) : UIObject(inGame) {
+ _type = UI_EDIT;
+
+ _fontSelected = NULL;
+
+ _selStart = _selEnd = 10000;
+ _scrollOffset = 0;
+
+ _cursorChar = NULL;
+ setCursorChar("|");
+
+ _cursorBlinkRate = 600;
+
+ _frameWidth = 0;
+
+ setText("");
+
+ _lastBlinkTime = 0;
+ _cursorVisible = true;
+
+ _maxLength = -1;
+
+ _canFocus = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+UIEdit::~UIEdit() {
+ if (!_sharedFonts) {
+ if (_fontSelected) {
+ _gameRef->_fontStorage->removeFont(_fontSelected);
+ }
+ }
+
+ delete[] _cursorChar;
+ _cursorChar = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEdit::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "UIEdit::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing EDIT file '%s'", filename);
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(DISABLED)
+TOKEN_DEF(VISIBLE)
+TOKEN_DEF(BACK)
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(FONT_SELECTED)
+TOKEN_DEF(FONT)
+TOKEN_DEF(TEXT)
+TOKEN_DEF(X)
+TOKEN_DEF(Y)
+TOKEN_DEF(WIDTH)
+TOKEN_DEF(HEIGHT)
+TOKEN_DEF(CURSOR_BLINK_RATE)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(FRAME_WIDTH)
+TOKEN_DEF(NAME)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(PARENT_NOTIFY)
+TOKEN_DEF(MAX_LENGTH)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(EDIT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool UIEdit::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(DISABLED)
+ TOKEN_TABLE(VISIBLE)
+ TOKEN_TABLE(BACK)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(FONT_SELECTED)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(TEXT)
+ TOKEN_TABLE(X)
+ TOKEN_TABLE(Y)
+ TOKEN_TABLE(WIDTH)
+ TOKEN_TABLE(HEIGHT)
+ TOKEN_TABLE(CURSOR_BLINK_RATE)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(FRAME_WIDTH)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(PARENT_NOTIFY)
+ TOKEN_TABLE(MAX_LENGTH)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(EDIT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd = 2;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_EDIT) {
+ _gameRef->LOG(0, "'EDIT' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_BACK:
+ delete _back;
+ _back = new UITiledImage(_gameRef);
+ if (!_back || DID_FAIL(_back->loadFile((char *)params))) {
+ delete _back;
+ _back = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE:
+ delete _image;
+ _image = new BaseSprite(_gameRef);
+ if (!_image || DID_FAIL(_image->loadFile((char *)params))) {
+ delete _image;
+ _image = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT:
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ _font = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_font) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT_SELECTED:
+ if (_fontSelected) {
+ _gameRef->_fontStorage->removeFont(_fontSelected);
+ }
+ _fontSelected = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_fontSelected) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_TEXT:
+ setText((char *)params);
+ _gameRef->_stringTable->expand(&_text);
+ break;
+
+ case TOKEN_X:
+ parser.scanStr((char *)params, "%d", &_posX);
+ break;
+
+ case TOKEN_Y:
+ parser.scanStr((char *)params, "%d", &_posY);
+ break;
+
+ case TOKEN_WIDTH:
+ parser.scanStr((char *)params, "%d", &_width);
+ break;
+
+ case TOKEN_HEIGHT:
+ parser.scanStr((char *)params, "%d", &_height);
+ break;
+
+ case TOKEN_MAX_LENGTH:
+ parser.scanStr((char *)params, "%d", &_maxLength);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_CURSOR_BLINK_RATE:
+ parser.scanStr((char *)params, "%d", &_cursorBlinkRate);
+ break;
+
+ case TOKEN_FRAME_WIDTH:
+ parser.scanStr((char *)params, "%d", &_frameWidth);
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PARENT_NOTIFY:
+ parser.scanStr((char *)params, "%b", &_parentNotify);
+ break;
+
+ case TOKEN_DISABLED:
+ parser.scanStr((char *)params, "%b", &_disable);
+ break;
+
+ case TOKEN_VISIBLE:
+ parser.scanStr((char *)params, "%b", &_visible);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in EDIT definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading EDIT definition");
+ return STATUS_FAILED;
+ }
+
+ correctSize();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEdit::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "EDIT\n");
+ buffer->putTextIndent(indent, "{\n");
+
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ if (_back && _back->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename());
+ }
+
+ if (_image && _image->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename());
+ }
+
+ if (_font && _font->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename());
+ }
+ if (_fontSelected && _fontSelected->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT_SELECTED=\"%s\"\n", _fontSelected->getFilename());
+ }
+
+ if (_cursor && _cursor->getFilename()) {
+ buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename());
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ if (_text) {
+ buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ buffer->putTextIndent(indent + 2, "X=%d\n", _posX);
+ buffer->putTextIndent(indent + 2, "Y=%d\n", _posY);
+ buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width);
+ buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height);
+ buffer->putTextIndent(indent + 2, "MAX_LENGTH=%d\n", _maxLength);
+ buffer->putTextIndent(indent + 2, "CURSOR_BLINK_RATE=%d\n", _cursorBlinkRate);
+ buffer->putTextIndent(indent + 2, "FRAME_WIDTH=%d\n", _frameWidth);
+
+ buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE");
+
+ // scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // editor properties
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool UIEdit::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetSelectedFont
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetSelectedFont") == 0) {
+ stack->correctParams(1);
+
+ if (_fontSelected) {
+ _gameRef->_fontStorage->removeFont(_fontSelected);
+ }
+ _fontSelected = _gameRef->_fontStorage->addFont(stack->pop()->getString());
+ stack->pushBool(_fontSelected != NULL);
+
+ return STATUS_OK;
+ } else {
+ return UIObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *UIEdit::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("editor");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SelStart
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SelStart") {
+ _scValue->setInt(_selStart);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SelEnd
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SelEnd") {
+ _scValue->setInt(_selEnd);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CursorBlinkRate
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "CursorBlinkRate") {
+ _scValue->setInt(_cursorBlinkRate);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CursorChar
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "CursorChar") {
+ _scValue->setString(_cursorChar);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FrameWidth
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "FrameWidth") {
+ _scValue->setInt(_frameWidth);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MaxLength
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "MaxLength") {
+ _scValue->setInt(_maxLength);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Text
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Text") {
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ WideString wstr = StringUtil::ansiToWide(_text);
+ _scValue->setString(StringUtil::wideToUtf8(wstr).c_str());
+ } else {
+ _scValue->setString(_text);
+ }
+ return _scValue;
+ } else {
+ return UIObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEdit::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // SelStart
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SelStart") == 0) {
+ _selStart = value->getInt();
+ _selStart = MAX(_selStart, 0);
+ _selStart = (int)MIN((size_t)_selStart, strlen(_text));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SelEnd
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SelEnd") == 0) {
+ _selEnd = value->getInt();
+ _selEnd = MAX(_selEnd, 0);
+ _selEnd = (int)MIN((size_t)_selEnd, strlen(_text));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CursorBlinkRate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CursorBlinkRate") == 0) {
+ _cursorBlinkRate = (uint32)value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CursorChar
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CursorChar") == 0) {
+ setCursorChar(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FrameWidth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FrameWidth") == 0) {
+ _frameWidth = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MaxLength
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MaxLength") == 0) {
+ _maxLength = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Text
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Text") == 0) {
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ WideString wstr = StringUtil::utf8ToWide(value->getString());
+ setText(StringUtil::wideToAnsi(wstr).c_str());
+ } else {
+ setText(value->getString());
+ }
+ return STATUS_OK;
+ } else {
+ return UIObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *UIEdit::scToString() {
+ return "[edit]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void UIEdit::setCursorChar(const char *character) {
+ if (!character) {
+ return;
+ }
+ delete[] _cursorChar;
+ _cursorChar = new char [strlen(character) + 1];
+ if (_cursorChar) {
+ strcpy(_cursorChar, character);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEdit::display(int offsetX, int offsetY) {
+ if (!_visible) {
+ return STATUS_OK;
+ }
+
+
+ // hack!
+ TTextEncoding OrigEncoding = _gameRef->_textEncoding;
+ _gameRef->_textEncoding = TEXT_ANSI;
+
+ if (_back) {
+ _back->display(offsetX + _posX, offsetY + _posY, _width, _height);
+ }
+ if (_image) {
+ _image->draw(offsetX + _posX, offsetY + _posY, NULL);
+ }
+
+ // prepare fonts
+ BaseFont *font;
+ BaseFont *sfont;
+
+ if (_font) {
+ font = _font;
+ } else {
+ font = _gameRef->_systemFont;
+ }
+
+ if (_fontSelected) {
+ sfont = _fontSelected;
+ } else {
+ sfont = font;
+ }
+
+ bool focused = isFocused();
+
+ _selStart = MAX(_selStart, 0);
+ _selEnd = MAX(_selEnd, 0);
+
+ _selStart = (int)MIN((size_t)_selStart, strlen(_text));
+ _selEnd = (int)MIN((size_t)_selEnd, strlen(_text));
+
+ //int CursorWidth = font->GetCharWidth(_cursorChar[0]);
+ int cursorWidth = font->getTextWidth((byte *)_cursorChar);
+
+ int s1, s2;
+ bool curFirst;
+ // modify scroll offset
+ if (_selStart >= _selEnd) {
+ while (font->getTextWidth((byte *)_text + _scrollOffset, MAX(0, _selEnd - _scrollOffset)) > _width - cursorWidth - 2 * _frameWidth) {
+ _scrollOffset++;
+ if (_scrollOffset >= (int)strlen(_text)) {
+ break;
+ }
+ }
+
+ _scrollOffset = MIN(_scrollOffset, _selEnd);
+
+ s1 = _selEnd;
+ s2 = _selStart;
+ curFirst = true;
+ } else {
+ while (font->getTextWidth((byte *)_text + _scrollOffset, MAX(0, _selStart - _scrollOffset)) +
+ sfont->getTextWidth((byte *)(_text + MAX(_scrollOffset, _selStart)), _selEnd - MAX(_scrollOffset, _selStart))
+
+ > _width - cursorWidth - 2 * _frameWidth) {
+ _scrollOffset++;
+ if (_scrollOffset >= (int)strlen(_text)) {
+ break;
+ }
+ }
+
+ _scrollOffset = MIN(_scrollOffset, _selEnd);
+
+ s1 = _selStart;
+ s2 = _selEnd;
+ curFirst = false;
+ }
+
+
+ int alignOffset = 0;
+
+ for (int count = 0; count < 2; count++) {
+ // draw text
+ int xxx, yyy, width, height;
+
+ xxx = _posX + _frameWidth + offsetX;
+ yyy = _posY + _frameWidth + offsetY;
+
+ width = _posX + _width + offsetX - _frameWidth;
+ height = MAX(font->getLetterHeight(), sfont->getLetterHeight());
+
+ if (_gameRef->_textRTL) {
+ xxx += alignOffset;
+ }
+
+ TTextAlign align = TAL_LEFT;
+
+
+ // unselected 1
+ if (s1 > _scrollOffset) {
+ if (count) {
+ font->drawText((byte *)_text + _scrollOffset, xxx, yyy, width - xxx, align, height, s1 - _scrollOffset);
+ }
+ xxx += font->getTextWidth((byte *)_text + _scrollOffset, s1 - _scrollOffset);
+ alignOffset += font->getTextWidth((byte *)_text + _scrollOffset, s1 - _scrollOffset);
+ }
+
+ // cursor
+ if (focused && curFirst) {
+ if (count) {
+ if (g_system->getMillis() - _lastBlinkTime >= _cursorBlinkRate) {
+ _lastBlinkTime = g_system->getMillis();
+ _cursorVisible = !_cursorVisible;
+ }
+ if (_cursorVisible) {
+ font->drawText((byte *)_cursorChar, xxx, yyy, width - xxx, align, height, 1);
+ }
+ }
+ xxx += cursorWidth;
+ alignOffset += cursorWidth;
+ }
+
+ // selected
+ int s3 = MAX(s1, _scrollOffset);
+
+ if (s2 - s3 > 0) {
+ if (count) {
+ sfont->drawText((byte *)_text + s3, xxx, yyy, width - xxx, align, height, s2 - s3);
+ }
+ xxx += sfont->getTextWidth((byte *)_text + s3, s2 - s3);
+ alignOffset += sfont->getTextWidth((byte *)_text + s3, s2 - s3);
+ }
+
+ // cursor
+ if (focused && !curFirst) {
+ if (count) {
+ if (g_system->getMillis() - _lastBlinkTime >= _cursorBlinkRate) {
+ _lastBlinkTime = g_system->getMillis();
+ _cursorVisible = !_cursorVisible;
+ }
+ if (_cursorVisible) {
+ font->drawText((byte *)_cursorChar, xxx, yyy, width - xxx, align, height, 1);
+ }
+ }
+ xxx += cursorWidth;
+ alignOffset += cursorWidth;
+ }
+
+ // unselected 2
+ if (count) {
+ font->drawText((byte *)_text + s2, xxx, yyy, width - xxx, align, height);
+ }
+ alignOffset += font->getTextWidth((byte *)_text + s2);
+
+ alignOffset = (_width - 2 * _frameWidth) - alignOffset;
+ if (alignOffset < 0) {
+ alignOffset = 0;
+ }
+ }
+
+
+ _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, offsetX + _posX, offsetY + _posY, _width, _height, 100, 100, false));
+
+
+ _gameRef->_textEncoding = OrigEncoding;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEdit::handleKeypress(Common::Event *event, bool printable) {
+ bool handled = false;
+
+ if (event->type == Common::EVENT_KEYDOWN && !printable) {
+ switch (event->kbd.keycode) {
+ case Common::KEYCODE_ESCAPE:
+ case Common::KEYCODE_TAB:
+ case Common::KEYCODE_RETURN:
+ return false;
+
+ // ctrl+A
+ case Common::KEYCODE_a:
+ if (BaseKeyboardState::isControlDown()) {
+ _selStart = 0;
+ _selEnd = strlen(_text);
+ handled = true;
+ }
+ break;
+
+ case Common::KEYCODE_BACKSPACE:
+ if (_selStart == _selEnd) {
+ if (_gameRef->_textRTL) {
+ deleteChars(_selStart, _selStart + 1);
+ } else {
+ deleteChars(_selStart - 1, _selStart);
+ }
+ } else {
+ deleteChars(_selStart, _selEnd);
+ }
+ if (_selEnd >= _selStart) {
+ _selEnd -= MAX(1, _selEnd - _selStart);
+ }
+ _selStart = _selEnd;
+
+ handled = true;
+ break;
+
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_UP:
+ _selEnd--;
+ if (!BaseKeyboardState::isShiftDown()) {
+ _selStart = _selEnd;
+ }
+ handled = true;
+ break;
+
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_DOWN:
+ _selEnd++;
+ if (!BaseKeyboardState::isShiftDown()) {
+ _selStart = _selEnd;
+ }
+ handled = true;
+ break;
+
+ case Common::KEYCODE_HOME:
+ if (_gameRef->_textRTL) {
+ _selEnd = strlen(_text);
+ if (!BaseKeyboardState::isShiftDown()) {
+ _selStart = _selEnd;
+ }
+ } else {
+ _selEnd = 0;
+ if (!BaseKeyboardState::isShiftDown()) {
+ _selStart = _selEnd;
+ }
+ }
+ handled = true;
+ break;
+
+ case Common::KEYCODE_END:
+ if (_gameRef->_textRTL) {
+ _selEnd = 0;
+ if (!BaseKeyboardState::isShiftDown()) {
+ _selStart = _selEnd;
+ }
+ } else {
+ _selEnd = strlen(_text);
+ if (!BaseKeyboardState::isShiftDown()) {
+ _selStart = _selEnd;
+ }
+ }
+ handled = true;
+ break;
+
+ case Common::KEYCODE_DELETE:
+ if (_selStart == _selEnd) {
+ if (_gameRef->_textRTL) {
+ deleteChars(_selStart - 1, _selStart);
+ _selEnd--;
+ if (_selEnd < 0) {
+ _selEnd = 0;
+ }
+ } else {
+ deleteChars(_selStart, _selStart + 1);
+ }
+ } else {
+ deleteChars(_selStart, _selEnd);
+ }
+ if (_selEnd > _selStart) {
+ _selEnd -= (_selEnd - _selStart);
+ }
+
+ _selStart = _selEnd;
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ return handled;
+ } else if (event->type == Common::EVENT_KEYDOWN && printable) {
+ if (_selStart != _selEnd) {
+ deleteChars(_selStart, _selEnd);
+ }
+
+ //WideString wstr = StringUtil::Utf8ToWide(event->kbd.ascii);
+ WideString wstr;
+ wstr += (char)event->kbd.ascii;
+ _selEnd += insertChars(_selEnd, (const byte *)StringUtil::wideToAnsi(wstr).c_str(), 1);
+
+ if (_gameRef->_textRTL) {
+ _selEnd = _selStart;
+ } else {
+ _selStart = _selEnd;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+int UIEdit::deleteChars(int start, int end) {
+ if (start > end) {
+ BaseUtils::swap(&start, &end);
+ }
+
+ start = MAX(start, (int)0);
+ end = MIN((size_t)end, strlen(_text));
+
+ char *str = new char[strlen(_text) - (end - start) + 1];
+ if (str) {
+ if (start > 0) {
+ memcpy(str, _text, start);
+ }
+ memcpy(str + MAX(0, start), _text + end, strlen(_text) - end + 1);
+
+ delete[] _text;
+ _text = str;
+ }
+ if (_parentNotify && _parent) {
+ _parent->applyEvent(getName());
+ }
+
+ return end - start;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int UIEdit::insertChars(int pos, const byte *chars, int num) {
+ if ((int)strlen(_text) + num > _maxLength) {
+ num -= (strlen(_text) + num - _maxLength);
+ }
+
+ pos = MAX(pos, (int)0);
+ pos = MIN((size_t)pos, strlen(_text));
+
+ char *str = new char[strlen(_text) + num + 1];
+ if (str) {
+ if (pos > 0) {
+ memcpy(str, _text, pos);
+ }
+ memcpy(str + pos + num, _text + pos, strlen(_text) - pos + 1);
+
+ memcpy(str + pos, chars, num);
+
+ delete[] _text;
+ _text = str;
+ }
+ if (_parentNotify && _parent) {
+ _parent->applyEvent(getName());
+ }
+
+ return num;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEdit::persist(BasePersistenceManager *persistMgr) {
+
+ UIObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_cursorBlinkRate));
+ persistMgr->transfer(TMEMBER(_cursorChar));
+ persistMgr->transfer(TMEMBER(_fontSelected));
+ persistMgr->transfer(TMEMBER(_frameWidth));
+ persistMgr->transfer(TMEMBER(_maxLength));
+ persistMgr->transfer(TMEMBER(_scrollOffset));
+ persistMgr->transfer(TMEMBER(_selEnd));
+ persistMgr->transfer(TMEMBER(_selStart));
+
+ if (!persistMgr->getIsSaving()) {
+ _cursorVisible = false;
+ _lastBlinkTime = 0;
+ }
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ui/ui_edit.h b/engines/wintermute/ui/ui_edit.h
new file mode 100644
index 0000000000..5bb31422b6
--- /dev/null
+++ b/engines/wintermute/ui/ui_edit.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_UIEDIT_H
+#define WINTERMUTE_UIEDIT_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/ui/ui_object.h"
+#include "common/events.h"
+
+namespace Wintermute {
+class BaseFont;
+class UIEdit : public UIObject {
+public:
+ DECLARE_PERSISTENT(UIEdit, UIObject)
+ int _maxLength;
+ int insertChars(int pos, const byte *chars, int num);
+ int deleteChars(int start, int end);
+ bool _cursorVisible;
+ uint32 _lastBlinkTime;
+ virtual bool display(int offsetX, int offsetY);
+ virtual bool handleKeypress(Common::Event *event, bool printable = false);
+ int _scrollOffset;
+ int _frameWidth;
+ uint32 _cursorBlinkRate;
+ void setCursorChar(const char *character);
+ char *_cursorChar;
+ int _selEnd;
+ int _selStart;
+ BaseFont *_fontSelected;
+ UIEdit(BaseGame *inGame);
+ virtual ~UIEdit();
+
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ui/ui_entity.cpp b/engines/wintermute/ui/ui_entity.cpp
new file mode 100644
index 0000000000..1cb4e0926b
--- /dev/null
+++ b/engines/wintermute/ui/ui_entity.cpp
@@ -0,0 +1,366 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ad/ad_entity.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/ui/ui_entity.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(UIEntity, false)
+
+//////////////////////////////////////////////////////////////////////////
+UIEntity::UIEntity(BaseGame *inGame) : UIObject(inGame) {
+ _type = UI_CUSTOM;
+ _entity = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+UIEntity::~UIEntity() {
+ if (_entity) {
+ _gameRef->unregisterObject(_entity);
+ }
+ _entity = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEntity::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "UIEntity::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing ENTITY container file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(ENTITY_CONTAINER)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(DISABLED)
+TOKEN_DEF(VISIBLE)
+TOKEN_DEF(X)
+TOKEN_DEF(Y)
+TOKEN_DEF(NAME)
+TOKEN_DEF(ENTITY)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool UIEntity::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(ENTITY_CONTAINER)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(DISABLED)
+ TOKEN_TABLE(VISIBLE)
+ TOKEN_TABLE(X)
+ TOKEN_TABLE(Y)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(ENTITY)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd = 2;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_ENTITY_CONTAINER) {
+ _gameRef->LOG(0, "'ENTITY_CONTAINER' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_X:
+ parser.scanStr((char *)params, "%d", &_posX);
+ break;
+
+ case TOKEN_Y:
+ parser.scanStr((char *)params, "%d", &_posY);
+ break;
+
+ case TOKEN_DISABLED:
+ parser.scanStr((char *)params, "%b", &_disable);
+ break;
+
+ case TOKEN_VISIBLE:
+ parser.scanStr((char *)params, "%b", &_visible);
+ break;
+
+ case TOKEN_ENTITY:
+ if (DID_FAIL(setEntity((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in ENTITY_CONTAINER definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading ENTITY_CONTAINER definition");
+ return STATUS_FAILED;
+ }
+
+ correctSize();
+
+ if (_gameRef->_editorMode) {
+ _width = 50;
+ _height = 50;
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEntity::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "ENTITY_CONTAINER\n");
+ buffer->putTextIndent(indent, "{\n");
+
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ buffer->putTextIndent(indent + 2, "X=%d\n", _posX);
+ buffer->putTextIndent(indent + 2, "Y=%d\n", _posY);
+
+ buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE");
+
+ if (_entity && _entity->getFilename()) {
+ buffer->putTextIndent(indent + 2, "ENTITY=\"%s\"\n", _entity->getFilename());
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // editor properties
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEntity::setEntity(const char *filename) {
+ if (_entity) {
+ _gameRef->unregisterObject(_entity);
+ }
+ _entity = new AdEntity(_gameRef);
+ if (!_entity || DID_FAIL(_entity->loadFile(filename))) {
+ delete _entity;
+ _entity = NULL;
+ return STATUS_FAILED;
+ } else {
+ _entity->_nonIntMouseEvents = true;
+ _entity->_sceneIndependent = true;
+ _entity->makeFreezable(false);
+ _gameRef->registerObject(_entity);
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEntity::display(int offsetX, int offsetY) {
+ if (!_visible) {
+ return STATUS_OK;
+ }
+
+ if (_entity) {
+ _entity->_posX = offsetX + _posX;
+ _entity->_posY = offsetY + _posY;
+ if (_entity->_scale < 0) {
+ _entity->_zoomable = false;
+ }
+ _entity->_shadowable = false;
+
+ _entity->update();
+
+ bool origReg = _entity->_registrable;
+
+ if (_entity->_registrable && _disable) {
+ _entity->_registrable = false;
+ }
+
+ _entity->display();
+ _entity->_registrable = origReg;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool UIEntity::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // GetEntity
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetEntity") == 0) {
+ stack->correctParams(0);
+
+ if (_entity) {
+ stack->pushNative(_entity, true);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetEntity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetEntity") == 0) {
+ stack->correctParams(1);
+
+ const char *filename = stack->pop()->getString();
+
+ if (DID_SUCCEED(setEntity(filename))) {
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ } else {
+ return UIObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *UIEntity::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("entity container");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Freezable
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Freezable") {
+ if (_entity) {
+ _scValue->setBool(_entity->_freezable);
+ } else {
+ _scValue->setBool(false);
+ }
+ return _scValue;
+ } else {
+ return UIObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEntity::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Freezable
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Freezable") == 0) {
+ if (_entity) {
+ _entity->makeFreezable(value->getBool());
+ }
+ return STATUS_OK;
+ } else {
+ return UIObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *UIEntity::scToString() {
+ return "[entity container]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIEntity::persist(BasePersistenceManager *persistMgr) {
+
+ UIObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_entity));
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ui/ui_entity.h b/engines/wintermute/ui/ui_entity.h
new file mode 100644
index 0000000000..b5f4450071
--- /dev/null
+++ b/engines/wintermute/ui/ui_entity.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_UIENTITY_H
+#define WINTERMUTE_UIENTITY_H
+
+#include "engines/wintermute/ui/ui_object.h"
+
+namespace Wintermute {
+class AdEntity;
+class UIEntity : public UIObject {
+public:
+ DECLARE_PERSISTENT(UIEntity, UIObject)
+ UIEntity(BaseGame *inGame);
+ virtual ~UIEntity();
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ virtual bool display() { return display(0, 0); }
+ virtual bool display(int offsetX, int offsetY);
+ AdEntity *_entity;
+ bool setEntity(const char *filename);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ui/ui_object.cpp b/engines/wintermute/ui/ui_object.cpp
new file mode 100644
index 0000000000..8e5bae993c
--- /dev/null
+++ b/engines/wintermute/ui/ui_object.cpp
@@ -0,0 +1,651 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/ui/ui_object.h"
+#include "engines/wintermute/ui/ui_tiled_image.h"
+#include "engines/wintermute/ui/ui_window.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(UIObject, false)
+
+//////////////////////////////////////////////////////////////////////////
+UIObject::UIObject(BaseGame *inGame) : BaseObject(inGame) {
+ _back = NULL;
+ _image = NULL;
+ _font = NULL;
+ _text = NULL;
+ _sharedFonts = _sharedImages = false;
+
+ _width = _height = 0;
+
+ _listenerObject = NULL;
+ _listenerParamObject = NULL;
+ _listenerParamDWORD = 0;
+
+ _disable = false;
+ _visible = true;
+
+ _type = UI_UNKNOWN;
+ _parent = NULL;
+
+ _parentNotify = false;
+
+ _focusedWidget = NULL;
+
+ _canFocus = false;
+ _nonIntMouseEvents = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+UIObject::~UIObject() {
+ if (!_gameRef->_loadInProgress) {
+ SystemClassRegistry::getInstance()->enumInstances(BaseGame::invalidateValues, "ScValue", (void *)this);
+ }
+
+ if (_back) {
+ delete _back;
+ }
+ if (_font && !_sharedFonts) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+
+ if (_image && !_sharedImages) {
+ delete _image;
+ }
+
+ if (_text) {
+ delete[] _text;
+ }
+
+ _focusedWidget = NULL; // ref only
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void UIObject::setText(const char *text) {
+ if (_text) {
+ delete[] _text;
+ }
+ _text = new char [strlen(text) + 1];
+ if (_text) {
+ strcpy(_text, text);
+ for (uint32 i = 0; i < strlen(_text); i++) {
+ if (_text[i] == '|') {
+ _text[i] = '\n';
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::display(int offsetX, int offsetY) {
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void UIObject::setListener(BaseScriptHolder *object, BaseScriptHolder *listenerObject, uint32 listenerParam) {
+ _listenerObject = object;
+ _listenerParamObject = listenerObject;
+ _listenerParamDWORD = listenerParam;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void UIObject::correctSize() {
+ Rect32 rect;
+
+ if (_width <= 0) {
+ if (_image) {
+ _image->getBoundingRect(&rect, 0, 0);
+ _width = rect.right - rect.left;
+ } else {
+ _width = 100;
+ }
+ }
+
+ if (_height <= 0) {
+ if (_image) {
+ _image->getBoundingRect(&rect, 0, 0);
+ _height = rect.bottom - rect.top;
+ }
+ }
+
+ if (_back) {
+ _back->correctSize(&_width, &_height);
+ }
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetFont
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetFont") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ if (val->isNULL()) {
+ _font = NULL;
+ stack->pushBool(true);
+ } else {
+ _font = _gameRef->_fontStorage->addFont(val->getString());
+ stack->pushBool(_font != NULL);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetImage") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ /* const char *filename = */ val->getString();
+
+ delete _image;
+ _image = NULL;
+ if (val->isNULL()) {
+ stack->pushBool(true);
+ return STATUS_OK;
+ }
+
+ _image = new BaseSprite(_gameRef);
+ if (!_image || DID_FAIL(_image->loadFile(val->getString()))) {
+ delete _image;
+ _image = NULL;
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetImage") == 0) {
+ stack->correctParams(0);
+ if (!_image || !_image->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_image->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetImageObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetImageObject") == 0) {
+ stack->correctParams(0);
+ if (!_image) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_image, true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Focus
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Focus") == 0) {
+ stack->correctParams(0);
+ focus();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MoveAfter / MoveBefore
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MoveAfter") == 0 || strcmp(name, "MoveBefore") == 0) {
+ stack->correctParams(1);
+
+ if (_parent && _parent->_type == UI_WINDOW) {
+ UIWindow *win = (UIWindow *)_parent;
+
+ uint32 i;
+ bool found = false;
+ ScValue *val = stack->pop();
+ // find directly
+ if (val->isNative()) {
+ UIObject *widget = (UIObject *)val->getNative();
+ for (i = 0; i < win->_widgets.size(); i++) {
+ if (win->_widgets[i] == widget) {
+ found = true;
+ break;
+ }
+ }
+ }
+ // find by name
+ else {
+ const char *findName = val->getString();
+ for (i = 0; i < win->_widgets.size(); i++) {
+ if (scumm_stricmp(win->_widgets[i]->getName(), findName) == 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (found) {
+ bool done = false;
+ for (uint32 j = 0; j < win->_widgets.size(); j++) {
+ if (win->_widgets[j] == this) {
+ if (strcmp(name, "MoveAfter") == 0) {
+ i++;
+ }
+ if (j >= i) {
+ j++;
+ }
+
+ win->_widgets.insert_at(i, this);
+ win->_widgets.remove_at(j);
+
+ done = true;
+ stack->pushBool(true);
+ break;
+ }
+ }
+ if (!done) {
+ stack->pushBool(false);
+ }
+ } else {
+ stack->pushBool(false);
+ }
+
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MoveToBottom
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MoveToBottom") == 0) {
+ stack->correctParams(0);
+
+ if (_parent && _parent->_type == UI_WINDOW) {
+ UIWindow *win = (UIWindow *)_parent;
+ for (uint32 i = 0; i < win->_widgets.size(); i++) {
+ if (win->_widgets[i] == this) {
+ win->_widgets.remove_at(i);
+ win->_widgets.insert_at(0, this);
+ break;
+ }
+ }
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // MoveToTop
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "MoveToTop") == 0) {
+ stack->correctParams(0);
+
+ if (_parent && _parent->_type == UI_WINDOW) {
+ UIWindow *win = (UIWindow *)_parent;
+ for (uint32 i = 0; i < win->_widgets.size(); i++) {
+ if (win->_widgets[i] == this) {
+ win->_widgets.remove_at(i);
+ win->_widgets.add(this);
+ break;
+ }
+ }
+ stack->pushBool(true);
+ } else {
+ stack->pushBool(false);
+ }
+
+ return STATUS_OK;
+ } else {
+ return BaseObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *UIObject::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("ui_object");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Name") {
+ _scValue->setString(getName());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Parent (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Parent") {
+ _scValue->setNative(_parent, true);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ParentNotify
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ParentNotify") {
+ _scValue->setBool(_parentNotify);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Width
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Width") {
+ _scValue->setInt(_width);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Height
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Height") {
+ _scValue->setInt(_height);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Visible
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Visible") {
+ _scValue->setBool(_visible);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Disabled
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Disabled") {
+ _scValue->setBool(_disable);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Text
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Text") {
+ _scValue->setString(_text);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NextSibling (RO) / PrevSibling (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NextSibling" || name == "PrevSibling") {
+ _scValue->setNULL();
+ if (_parent && _parent->_type == UI_WINDOW) {
+ UIWindow *win = (UIWindow *)_parent;
+ for (uint32 i = 0; i < win->_widgets.size(); i++) {
+ if (win->_widgets[i] == this) {
+ if (name == "NextSibling") {
+ if (i < win->_widgets.size() - 1) {
+ _scValue->setNative(win->_widgets[i + 1], true);
+ }
+ } else {
+ if (i > 0) {
+ _scValue->setNative(win->_widgets[i - 1], true);
+ }
+ }
+ break;
+ }
+ }
+ }
+ return _scValue;
+ } else {
+ return BaseObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ParentNotify
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ParentNotify") == 0) {
+ _parentNotify = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Width
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Width") == 0) {
+ _width = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Height
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Height") == 0) {
+ _height = value->getInt();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Visible
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Visible") == 0) {
+ _visible = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Disabled
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Disabled") == 0) {
+ _disable = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Text
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Text") == 0) {
+ setText(value->getString());
+ return STATUS_OK;
+ } else {
+ return BaseObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *UIObject::scToString() {
+ return "[ui_object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::isFocused() {
+ if (!_gameRef->_focusedWindow) {
+ return false;
+ }
+ if (_gameRef->_focusedWindow == this) {
+ return true;
+ }
+
+ UIObject *obj = _gameRef->_focusedWindow;
+ while (obj) {
+ if (obj == this) {
+ return true;
+ } else {
+ obj = obj->_focusedWidget;
+ }
+ }
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::handleMouse(TMouseEvent event, TMouseButton button) {
+ // handle focus change
+ if (event == MOUSE_CLICK && button == MOUSE_BUTTON_LEFT) {
+ focus();
+ }
+ return BaseObject::handleMouse(event, button);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::focus() {
+ UIObject *obj = this;
+ bool disabled = false;
+ while (obj) {
+ if (obj->_disable && obj->_type == UI_WINDOW) {
+ disabled = true;
+ break;
+ }
+ obj = obj->_parent;
+ }
+ if (!disabled) {
+ obj = this;
+ while (obj) {
+ if (obj->_parent) {
+ if (!obj->_disable && obj->_canFocus) {
+ obj->_parent->_focusedWidget = obj;
+ }
+ } else {
+ if (obj->_type == UI_WINDOW) {
+ _gameRef->focusWindow((UIWindow *)obj);
+ }
+ }
+
+ obj = obj->_parent;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::getTotalOffset(int *offsetX, int *offsetY) {
+ int offX = 0, offY = 0;
+
+ UIObject *obj = _parent;
+ while (obj) {
+ offX += obj->_posX;
+ offY += obj->_posY;
+
+ obj = obj->_parent;
+ }
+ if (offsetX) {
+ *offsetX = offX;
+ }
+ if (offsetY) {
+ *offsetY = offY;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::persist(BasePersistenceManager *persistMgr) {
+
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_back));
+ persistMgr->transfer(TMEMBER(_canFocus));
+ persistMgr->transfer(TMEMBER(_disable));
+ persistMgr->transfer(TMEMBER(_focusedWidget));
+ persistMgr->transfer(TMEMBER(_font));
+ persistMgr->transfer(TMEMBER(_height));
+ persistMgr->transfer(TMEMBER(_image));
+ persistMgr->transfer(TMEMBER(_listenerObject));
+ persistMgr->transfer(TMEMBER(_listenerParamObject));
+ persistMgr->transfer(TMEMBER(_listenerParamDWORD));
+ persistMgr->transfer(TMEMBER(_parent));
+ persistMgr->transfer(TMEMBER(_parentNotify));
+ persistMgr->transfer(TMEMBER(_sharedFonts));
+ persistMgr->transfer(TMEMBER(_sharedImages));
+ persistMgr->transfer(TMEMBER(_text));
+ persistMgr->transfer(TMEMBER_INT(_type));
+ persistMgr->transfer(TMEMBER(_visible));
+ persistMgr->transfer(TMEMBER(_width));
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIObject::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ return STATUS_FAILED;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ui/ui_object.h b/engines/wintermute/ui/ui_object.h
new file mode 100644
index 0000000000..ec2ea33de1
--- /dev/null
+++ b/engines/wintermute/ui/ui_object.h
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_UIOBJECT_H
+#define WINTERMUTE_UIOBJECT_H
+
+
+#include "engines/wintermute/base/base_object.h"
+#include "engines/wintermute/dctypes.h" // Added by ClassView
+
+namespace Wintermute {
+
+class UITiledImage;
+class BaseFont;
+class UIObject : public BaseObject {
+public:
+
+ bool getTotalOffset(int *offsetX, int *offsetY);
+ bool _canFocus;
+ bool focus();
+ virtual bool handleMouse(TMouseEvent event, TMouseButton button);
+ bool isFocused();
+ bool _parentNotify;
+ DECLARE_PERSISTENT(UIObject, BaseObject)
+ UIObject *_parent;
+ virtual bool display() { return display(0, 0); }
+ virtual bool display(int offsetX) { return display(offsetX, 0); }
+ virtual bool display(int offsetX, int offsetY);
+ virtual void correctSize();
+ bool _sharedFonts;
+ bool _sharedImages;
+ void setText(const char *text);
+ char *_text;
+ BaseFont *_font;
+ bool _visible;
+ UITiledImage *_back;
+ bool _disable;
+ UIObject(BaseGame *inGame = NULL);
+ virtual ~UIObject();
+ int _width;
+ int _height;
+ TUIObjectType _type;
+ BaseSprite *_image;
+ void setListener(BaseScriptHolder *object, BaseScriptHolder *listenerObject, uint32 listenerParam);
+ BaseScriptHolder *_listenerParamObject;
+ uint32 _listenerParamDWORD;
+ BaseScriptHolder *_listenerObject;
+ UIObject *_focusedWidget;
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ui/ui_text.cpp b/engines/wintermute/ui/ui_text.cpp
new file mode 100644
index 0000000000..2c10f176c7
--- /dev/null
+++ b/engines/wintermute/ui/ui_text.cpp
@@ -0,0 +1,522 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/ui/ui_text.h"
+#include "engines/wintermute/ui/ui_tiled_image.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/platform_osystem.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(UIText, false)
+
+//////////////////////////////////////////////////////////////////////////
+UIText::UIText(BaseGame *inGame) : UIObject(inGame) {
+ _textAlign = TAL_LEFT;
+ _verticalAlign = VAL_CENTER;
+ _type = UI_STATIC;
+ _canFocus = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+UIText::~UIText() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIText::display(int offsetX, int offsetY) {
+ if (!_visible) {
+ return STATUS_OK;
+ }
+
+
+ BaseFont *font = _font;
+ if (!font) {
+ font = _gameRef->_systemFont;
+ }
+
+ if (_back) {
+ _back->display(offsetX + _posX, offsetY + _posY, _width, _height);
+ }
+ if (_image) {
+ _image->draw(offsetX + _posX, offsetY + _posY, NULL);
+ }
+
+ if (font && _text) {
+ int textOffset;
+ switch (_verticalAlign) {
+ case VAL_TOP:
+ textOffset = 0;
+ break;
+ case VAL_BOTTOM:
+ textOffset = _height - font->getTextHeight((byte *)_text, _width);
+ break;
+ default:
+ textOffset = (_height - font->getTextHeight((byte *)_text, _width)) / 2;
+ }
+ font->drawText((byte *)_text, offsetX + _posX, offsetY + _posY + textOffset, _width, _textAlign, _height);
+ }
+
+ //_gameRef->_renderer->_rectList.add(new BaseActiveRect(_gameRef, this, NULL, OffsetX + _posX, OffsetY + _posY, _width, _height, 100, 100, false));
+
+ return STATUS_OK;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIText::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "UIText::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing STATIC file '%s'", filename);
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(STATIC)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(DISABLED)
+TOKEN_DEF(VISIBLE)
+TOKEN_DEF(BACK)
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(FONT)
+TOKEN_DEF(TEXT_ALIGN)
+TOKEN_DEF(VERTICAL_ALIGN)
+TOKEN_DEF(TEXT)
+TOKEN_DEF(X)
+TOKEN_DEF(Y)
+TOKEN_DEF(WIDTH)
+TOKEN_DEF(HEIGHT)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(NAME)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PARENT_NOTIFY)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool UIText::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(STATIC)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(DISABLED)
+ TOKEN_TABLE(VISIBLE)
+ TOKEN_TABLE(BACK)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(TEXT_ALIGN)
+ TOKEN_TABLE(VERTICAL_ALIGN)
+ TOKEN_TABLE(TEXT)
+ TOKEN_TABLE(X)
+ TOKEN_TABLE(Y)
+ TOKEN_TABLE(WIDTH)
+ TOKEN_TABLE(HEIGHT)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PARENT_NOTIFY)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd = 2;
+ BaseParser parser;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_STATIC) {
+ _gameRef->LOG(0, "'STATIC' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_BACK:
+ delete _back;
+ _back = new UITiledImage(_gameRef);
+ if (!_back || DID_FAIL(_back->loadFile((char *)params))) {
+ delete _back;
+ _back = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE:
+ delete _image;
+ _image = new BaseSprite(_gameRef);
+ if (!_image || DID_FAIL(_image->loadFile((char *)params))) {
+ delete _image;
+ _image = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT:
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ _font = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_font) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_TEXT:
+ setText((char *)params);
+ _gameRef->_stringTable->expand(&_text);
+ break;
+
+ case TOKEN_TEXT_ALIGN:
+ if (scumm_stricmp((char *)params, "left") == 0) {
+ _textAlign = TAL_LEFT;
+ } else if (scumm_stricmp((char *)params, "right") == 0) {
+ _textAlign = TAL_RIGHT;
+ } else {
+ _textAlign = TAL_CENTER;
+ }
+ break;
+
+ case TOKEN_VERTICAL_ALIGN:
+ if (scumm_stricmp((char *)params, "top") == 0) {
+ _verticalAlign = VAL_TOP;
+ } else if (scumm_stricmp((char *)params, "bottom") == 0) {
+ _verticalAlign = VAL_BOTTOM;
+ } else {
+ _verticalAlign = VAL_CENTER;
+ }
+ break;
+
+ case TOKEN_X:
+ parser.scanStr((char *)params, "%d", &_posX);
+ break;
+
+ case TOKEN_Y:
+ parser.scanStr((char *)params, "%d", &_posY);
+ break;
+
+ case TOKEN_WIDTH:
+ parser.scanStr((char *)params, "%d", &_width);
+ break;
+
+ case TOKEN_HEIGHT:
+ parser.scanStr((char *)params, "%d", &_height);
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PARENT_NOTIFY:
+ parser.scanStr((char *)params, "%b", &_parentNotify);
+ break;
+
+ case TOKEN_DISABLED:
+ parser.scanStr((char *)params, "%b", &_disable);
+ break;
+
+ case TOKEN_VISIBLE:
+ parser.scanStr((char *)params, "%b", &_visible);
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in STATIC definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading STATIC definition");
+ return STATUS_FAILED;
+ }
+
+ correctSize();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIText::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "STATIC\n");
+ buffer->putTextIndent(indent, "{\n");
+
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ if (_back && _back->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename());
+ }
+
+ if (_image && _image->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename());
+ }
+
+ if (_font && _font->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename());
+ }
+
+ if (_cursor && _cursor->getFilename()) {
+ buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename());
+ }
+
+ if (_text) {
+ buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text);
+ }
+
+ switch (_textAlign) {
+ case TAL_LEFT:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left");
+ break;
+ case TAL_RIGHT:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right");
+ break;
+ case TAL_CENTER:
+ buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center");
+ break;
+ default:
+ error("UIText::SaveAsText - Unhandled enum");
+ break;
+ }
+
+ switch (_verticalAlign) {
+ case VAL_TOP:
+ buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "top");
+ break;
+ case VAL_BOTTOM:
+ buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "bottom");
+ break;
+ case VAL_CENTER:
+ buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "center");
+ break;
+ default:
+ error("UIText::SaveAsText - Unhandled enum value: NUM_VERTICAL_ALIGN");
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ buffer->putTextIndent(indent + 2, "X=%d\n", _posX);
+ buffer->putTextIndent(indent + 2, "Y=%d\n", _posY);
+ buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width);
+ buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height);
+
+ buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE");
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // editor properties
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool UIText::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SizeToFit
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SizeToFit") == 0) {
+ stack->correctParams(0);
+ sizeToFit();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HeightToFit
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "HeightToFit") == 0) {
+ stack->correctParams(0);
+ if (_font && _text) {
+ _height = _font->getTextHeight((byte *)_text, _width);
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ } else {
+ return UIObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *UIText::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("static");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TextAlign
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "TextAlign") {
+ _scValue->setInt(_textAlign);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // VerticalAlign
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "VerticalAlign") {
+ _scValue->setInt(_verticalAlign);
+ return _scValue;
+ } else {
+ return UIObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIText::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // TextAlign
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "TextAlign") == 0) {
+ int i = value->getInt();
+ if (i < 0 || i >= NUM_TEXT_ALIGN) {
+ i = 0;
+ }
+ _textAlign = (TTextAlign)i;
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // VerticalAlign
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "VerticalAlign") == 0) {
+ int i = value->getInt();
+ if (i < 0 || i >= NUM_VERTICAL_ALIGN) {
+ i = 0;
+ }
+ _verticalAlign = (TVerticalAlign)i;
+ return STATUS_OK;
+ } else {
+ return UIObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *UIText::scToString() {
+ return "[static]";
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIText::persist(BasePersistenceManager *persistMgr) {
+
+ UIObject::persist(persistMgr);
+ persistMgr->transfer(TMEMBER_INT(_textAlign));
+ persistMgr->transfer(TMEMBER_INT(_verticalAlign));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIText::sizeToFit() {
+ if (_font && _text) {
+ _width = _font->getTextWidth((byte *)_text);
+ _height = _font->getTextHeight((byte *)_text, _width);
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ui/ui_text.h b/engines/wintermute/ui/ui_text.h
new file mode 100644
index 0000000000..da4d113500
--- /dev/null
+++ b/engines/wintermute/ui/ui_text.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_UITEXT_H
+#define WINTERMUTE_UITEXT_H
+
+
+#include "engines/wintermute/ui/ui_object.h"
+
+namespace Wintermute {
+
+class UIText : public UIObject {
+private:
+ bool sizeToFit();
+public:
+ virtual bool display(int offsetX, int offsetY);
+ DECLARE_PERSISTENT(UIText, UIObject)
+ UIText(BaseGame *inGame = NULL);
+ virtual ~UIText();
+ TTextAlign _textAlign;
+ TVerticalAlign _verticalAlign;
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ui/ui_tiled_image.cpp b/engines/wintermute/ui/ui_tiled_image.cpp
new file mode 100644
index 0000000000..2b337330c7
--- /dev/null
+++ b/engines/wintermute/ui/ui_tiled_image.cpp
@@ -0,0 +1,394 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/ui/ui_tiled_image.h"
+#include "engines/wintermute/base/gfx/base_surface.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_sub_frame.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/platform_osystem.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(UITiledImage, false)
+
+//////////////////////////////////////////////////////////////////////////
+UITiledImage::UITiledImage(BaseGame *inGame) : BaseObject(inGame) {
+ _image = NULL;
+
+ BasePlatform::setRectEmpty(&_upLeft);
+ BasePlatform::setRectEmpty(&_upMiddle);
+ BasePlatform::setRectEmpty(&_upRight);
+ BasePlatform::setRectEmpty(&_middleLeft);
+ BasePlatform::setRectEmpty(&_middleMiddle);
+ BasePlatform::setRectEmpty(&_middleRight);
+ BasePlatform::setRectEmpty(&_downLeft);
+ BasePlatform::setRectEmpty(&_downMiddle);
+ BasePlatform::setRectEmpty(&_downRight);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+UITiledImage::~UITiledImage() {
+ delete _image;
+ _image = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UITiledImage::display(int x, int y, int width, int height) {
+ if (!_image) {
+ return STATUS_FAILED;
+ }
+
+ int tileWidth = _middleMiddle.right - _middleMiddle.left;
+ int tileHeight = _middleMiddle.bottom - _middleMiddle.top;
+
+ int nuColumns = (width - (_middleLeft.right - _middleLeft.left) - (_middleRight.right - _middleRight.left)) / tileWidth;
+ int nuRows = (height - (_upMiddle.bottom - _upMiddle.top) - (_downMiddle.bottom - _downMiddle.top)) / tileHeight;
+
+ int col, row;
+
+ _gameRef->_renderer->startSpriteBatch();
+
+ // top left/right
+ _image->_surface->displayTrans(x, y, _upLeft);
+ _image->_surface->displayTrans(x + (_upLeft.right - _upLeft.left) + nuColumns * tileWidth, y, _upRight);
+
+ // bottom left/right
+ _image->_surface->displayTrans(x, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downLeft);
+ _image->_surface->displayTrans(x + (_upLeft.right - _upLeft.left) + nuColumns * tileWidth, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downRight);
+
+ // left/right
+ int yyy = y + (_upMiddle.bottom - _upMiddle.top);
+ for (row = 0; row < nuRows; row++) {
+ _image->_surface->displayTrans(x, yyy, _middleLeft);
+ _image->_surface->displayTrans(x + (_middleLeft.right - _middleLeft.left) + nuColumns * tileWidth, yyy, _middleRight);
+ yyy += tileWidth;
+ }
+
+ // top/bottom
+ int xxx = x + (_upLeft.right - _upLeft.left);
+ for (col = 0; col < nuColumns; col++) {
+ _image->_surface->displayTrans(xxx, y, _upMiddle);
+ _image->_surface->displayTrans(xxx, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downMiddle);
+ xxx += tileWidth;
+ }
+
+ // tiles
+ yyy = y + (_upMiddle.bottom - _upMiddle.top);
+ for (row = 0; row < nuRows; row++) {
+ xxx = x + (_upLeft.right - _upLeft.left);
+ for (col = 0; col < nuColumns; col++) {
+ _image->_surface->displayTrans(xxx, yyy, _middleMiddle);
+ xxx += tileWidth;
+ }
+ yyy += tileWidth;
+ }
+
+ _gameRef->_renderer->endSpriteBatch();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UITiledImage::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "UITiledImage::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing TILED_IMAGE file '%s'", filename);
+ }
+
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(TILED_IMAGE)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(UP_LEFT)
+TOKEN_DEF(UP_RIGHT)
+TOKEN_DEF(UP_MIDDLE)
+TOKEN_DEF(DOWN_LEFT)
+TOKEN_DEF(DOWN_RIGHT)
+TOKEN_DEF(DOWN_MIDDLE)
+TOKEN_DEF(MIDDLE_LEFT)
+TOKEN_DEF(MIDDLE_RIGHT)
+TOKEN_DEF(MIDDLE_MIDDLE)
+TOKEN_DEF(VERTICAL_TILES)
+TOKEN_DEF(HORIZONTAL_TILES)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool UITiledImage::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(TILED_IMAGE)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(UP_LEFT)
+ TOKEN_TABLE(UP_RIGHT)
+ TOKEN_TABLE(UP_MIDDLE)
+ TOKEN_TABLE(DOWN_LEFT)
+ TOKEN_TABLE(DOWN_RIGHT)
+ TOKEN_TABLE(DOWN_MIDDLE)
+ TOKEN_TABLE(MIDDLE_LEFT)
+ TOKEN_TABLE(MIDDLE_RIGHT)
+ TOKEN_TABLE(MIDDLE_MIDDLE)
+ TOKEN_TABLE(VERTICAL_TILES)
+ TOKEN_TABLE(HORIZONTAL_TILES)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd;
+ BaseParser parser;
+ bool hTiles = false, vTiles = false;
+ int h1 = 0, h2 = 0, h3 = 0;
+ int v1 = 0, v2 = 0, v3 = 0;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_TILED_IMAGE) {
+ _gameRef->LOG(0, "'TILED_IMAGE' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) > 0) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE:
+ delete _image;
+ _image = new BaseSubFrame(_gameRef);
+ if (!_image || DID_FAIL(_image->setSurface((char *)params))) {
+ delete _image;
+ _image = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_UP_LEFT:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_upLeft.left, &_upLeft.top, &_upLeft.right, &_upLeft.bottom);
+ break;
+
+ case TOKEN_UP_RIGHT:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_upRight.left, &_upRight.top, &_upRight.right, &_upRight.bottom);
+ break;
+
+ case TOKEN_UP_MIDDLE:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_upMiddle.left, &_upMiddle.top, &_upMiddle.right, &_upMiddle.bottom);
+ break;
+
+ case TOKEN_DOWN_LEFT:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_downLeft.left, &_downLeft.top, &_downLeft.right, &_downLeft.bottom);
+ break;
+
+ case TOKEN_DOWN_RIGHT:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_downRight.left, &_downRight.top, &_downRight.right, &_downRight.bottom);
+ break;
+
+ case TOKEN_DOWN_MIDDLE:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_downMiddle.left, &_downMiddle.top, &_downMiddle.right, &_downMiddle.bottom);
+ break;
+
+ case TOKEN_MIDDLE_LEFT:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleLeft.left, &_middleLeft.top, &_middleLeft.right, &_middleLeft.bottom);
+ break;
+
+ case TOKEN_MIDDLE_RIGHT:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleRight.left, &_middleRight.top, &_middleRight.right, &_middleRight.bottom);
+ break;
+
+ case TOKEN_MIDDLE_MIDDLE:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleMiddle.left, &_middleMiddle.top, &_middleMiddle.right, &_middleMiddle.bottom);
+ break;
+
+ case TOKEN_HORIZONTAL_TILES:
+ parser.scanStr((char *)params, "%d,%d,%d", &h1, &h2, &h3);
+ hTiles = true;
+ break;
+
+ case TOKEN_VERTICAL_TILES:
+ parser.scanStr((char *)params, "%d,%d,%d", &v1, &v2, &v3);
+ vTiles = true;
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in TILED_IMAGE definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading TILED_IMAGE definition");
+ return STATUS_FAILED;
+ }
+
+ if (vTiles && hTiles) {
+ // up row
+ BasePlatform::setRect(&_upLeft, 0, 0, h1, v1);
+ BasePlatform::setRect(&_upMiddle, h1, 0, h1 + h2, v1);
+ BasePlatform::setRect(&_upRight, h1 + h2, 0, h1 + h2 + h3, v1);
+
+ // middle row
+ BasePlatform::setRect(&_middleLeft, 0, v1, h1, v1 + v2);
+ BasePlatform::setRect(&_middleMiddle, h1, v1, h1 + h2, v1 + v2);
+ BasePlatform::setRect(&_middleRight, h1 + h2, v1, h1 + h2 + h3, v1 + v2);
+
+ // down row
+ BasePlatform::setRect(&_downLeft, 0, v1 + v2, h1, v1 + v2 + v3);
+ BasePlatform::setRect(&_downMiddle, h1, v1 + v2, h1 + h2, v1 + v2 + v3);
+ BasePlatform::setRect(&_downRight, h1 + h2, v1 + v2, h1 + h2 + h3, v1 + v2 + v3);
+ }
+
+ // default
+ if (_image && _image->_surface) {
+ int width = _image->_surface->getWidth() / 3;
+ int height = _image->_surface->getHeight() / 3;
+
+ if (BasePlatform::isRectEmpty(&_upLeft)) {
+ BasePlatform::setRect(&_upLeft, 0, 0, width, height);
+ }
+ if (BasePlatform::isRectEmpty(&_upMiddle)) {
+ BasePlatform::setRect(&_upMiddle, width, 0, 2 * width, height);
+ }
+ if (BasePlatform::isRectEmpty(&_upRight)) {
+ BasePlatform::setRect(&_upRight, 2 * width, 0, 3 * width, height);
+ }
+
+ if (BasePlatform::isRectEmpty(&_middleLeft)) {
+ BasePlatform::setRect(&_middleLeft, 0, height, width, 2 * height);
+ }
+ if (BasePlatform::isRectEmpty(&_middleMiddle)) {
+ BasePlatform::setRect(&_middleMiddle, width, height, 2 * width, 2 * height);
+ }
+ if (BasePlatform::isRectEmpty(&_middleRight)) {
+ BasePlatform::setRect(&_middleRight, 2 * width, height, 3 * width, 2 * height);
+ }
+
+ if (BasePlatform::isRectEmpty(&_downLeft)) {
+ BasePlatform::setRect(&_downLeft, 0, 2 * height, width, 3 * height);
+ }
+ if (BasePlatform::isRectEmpty(&_downMiddle)) {
+ BasePlatform::setRect(&_downMiddle, width, 2 * height, 2 * width, 3 * height);
+ }
+ if (BasePlatform::isRectEmpty(&_downRight)) {
+ BasePlatform::setRect(&_downRight, 2 * width, 2 * height, 3 * width, 3 * height);
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UITiledImage::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "TILED_IMAGE\n");
+ buffer->putTextIndent(indent, "{\n");
+
+ if (_image && _image->getSurfaceFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getSurfaceFilename());
+ }
+
+ int h1, h2, h3;
+ int v1, v2, v3;
+
+ h1 = _upLeft.right;
+ h2 = _upMiddle.right - _upMiddle.left;
+ h3 = _upRight.right - _upRight.left;
+
+ v1 = _upLeft.bottom;
+ v2 = _middleLeft.bottom - _middleLeft.top;
+ v3 = _downLeft.bottom - _downLeft.top;
+
+
+ buffer->putTextIndent(indent + 2, "VERTICAL_TILES { %d, %d, %d }\n", v1, v2, v3);
+ buffer->putTextIndent(indent + 2, "HORIZONTAL_TILES { %d, %d, %d }\n", h1, h2, h3);
+
+ // editor properties
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void UITiledImage::correctSize(int *width, int *height) {
+ int tileWidth = _middleMiddle.right - _middleMiddle.left;
+ int tileHeight = _middleMiddle.bottom - _middleMiddle.top;
+
+ int nuColumns = (*width - (_middleLeft.right - _middleLeft.left) - (_middleRight.right - _middleRight.left)) / tileWidth;
+ int nuRows = (*height - (_upMiddle.bottom - _upMiddle.top) - (_downMiddle.bottom - _downMiddle.top)) / tileHeight;
+
+ *width = (_middleLeft.right - _middleLeft.left) + (_middleRight.right - _middleRight.left) + nuColumns * tileWidth;
+ *height = (_upMiddle.bottom - _upMiddle.top) + (_downMiddle.bottom - _downMiddle.top) + nuRows * tileHeight;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UITiledImage::persist(BasePersistenceManager *persistMgr) {
+ BaseObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_downLeft));
+ persistMgr->transfer(TMEMBER(_downMiddle));
+ persistMgr->transfer(TMEMBER(_downRight));
+ persistMgr->transfer(TMEMBER(_image));
+ persistMgr->transfer(TMEMBER(_middleLeft));
+ persistMgr->transfer(TMEMBER(_middleMiddle));
+ persistMgr->transfer(TMEMBER(_middleRight));
+ persistMgr->transfer(TMEMBER(_upLeft));
+ persistMgr->transfer(TMEMBER(_upMiddle));
+ persistMgr->transfer(TMEMBER(_upRight));
+
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ui/ui_tiled_image.h b/engines/wintermute/ui/ui_tiled_image.h
new file mode 100644
index 0000000000..c413e7f129
--- /dev/null
+++ b/engines/wintermute/ui/ui_tiled_image.h
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_UITILEDIMAGE_H
+#define WINTERMUTE_UITILEDIMAGE_H
+
+
+#include "engines/wintermute/ui/ui_object.h"
+#include "common/rect.h"
+
+namespace Wintermute {
+class BaseSubFrame;
+class UITiledImage : public BaseObject {
+public:
+ DECLARE_PERSISTENT(UITiledImage, BaseObject)
+ void correctSize(int *width, int *height);
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ bool display(int x, int y, int width, int height);
+ UITiledImage(BaseGame *inGame = NULL);
+ virtual ~UITiledImage();
+ BaseSubFrame *_image;
+ Rect32 _upLeft;
+ Rect32 _upMiddle;
+ Rect32 _upRight;
+ Rect32 _middleLeft;
+ Rect32 _middleMiddle;
+ Rect32 _middleRight;
+ Rect32 _downLeft;
+ Rect32 _downMiddle;
+ Rect32 _downRight;
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/ui/ui_window.cpp b/engines/wintermute/ui/ui_window.cpp
new file mode 100644
index 0000000000..9606486efb
--- /dev/null
+++ b/engines/wintermute/ui/ui_window.cpp
@@ -0,0 +1,1445 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_parser.h"
+#include "engines/wintermute/base/base_active_rect.h"
+#include "engines/wintermute/base/base_dynamic_buffer.h"
+#include "engines/wintermute/base/base_keyboard_state.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/ui/ui_button.h"
+#include "engines/wintermute/ui/ui_edit.h"
+#include "engines/wintermute/ui/ui_text.h"
+#include "engines/wintermute/ui/ui_tiled_image.h"
+#include "engines/wintermute/ui/ui_window.h"
+#include "engines/wintermute/base/base_viewport.h"
+#include "engines/wintermute/base/font/base_font_storage.h"
+#include "engines/wintermute/base/font/base_font.h"
+#include "engines/wintermute/base/base_string_table.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script.h"
+#include "engines/wintermute/base/scriptables/script_stack.h"
+#include "engines/wintermute/base/base_sprite.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/platform_osystem.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(UIWindow, false)
+
+//////////////////////////////////////////////////////////////////////////
+UIWindow::UIWindow(BaseGame *inGame) : UIObject(inGame) {
+ BasePlatform::setRectEmpty(&_titleRect);
+ BasePlatform::setRectEmpty(&_dragRect);
+ _titleAlign = TAL_LEFT;
+ _transparent = false;
+
+ _backInactive = NULL;
+ _fontInactive = NULL;
+ _imageInactive = NULL;
+
+ _type = UI_WINDOW;
+ _canFocus = true;
+
+ _dragging = false;
+ _dragFrom.x = _dragFrom.y = 0;
+
+ _mode = WINDOW_NORMAL;
+ _shieldWindow = NULL;
+ _shieldButton = NULL;
+
+ _fadeColor = 0x00000000;
+ _fadeBackground = false;
+
+ _ready = true;
+ _isMenu = false;
+ _inGame = false;
+
+ _clipContents = false;
+ _viewport = NULL;
+
+ _pauseMusic = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+UIWindow::~UIWindow() {
+ close();
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void UIWindow::cleanup() {
+ delete _shieldWindow;
+ delete _shieldButton;
+ delete _viewport;
+ _shieldWindow = NULL;
+ _shieldButton = NULL;
+ _viewport = NULL;
+
+ delete _backInactive;
+ if (!_sharedFonts && _fontInactive) {
+ _gameRef->_fontStorage->removeFont(_fontInactive);
+ }
+ if (!_sharedImages && _imageInactive) {
+ delete _imageInactive;
+ }
+
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ delete _widgets[i];
+ }
+ _widgets.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::display(int offsetX, int offsetY) {
+ // go exclusive
+ if (_mode == WINDOW_EXCLUSIVE || _mode == WINDOW_SYSTEM_EXCLUSIVE) {
+ if (!_shieldWindow) {
+ _shieldWindow = new UIWindow(_gameRef);
+ }
+ if (_shieldWindow) {
+ _shieldWindow->_posX = _shieldWindow->_posY = 0;
+ _shieldWindow->_width = _gameRef->_renderer->_width;
+ _shieldWindow->_height = _gameRef->_renderer->_height;
+
+ _shieldWindow->display();
+ }
+ } else if (_isMenu) {
+ if (!_shieldButton) {
+ _shieldButton = new UIButton(_gameRef);
+ _shieldButton->setName("close");
+ _shieldButton->setListener(this, _shieldButton, 0);
+ _shieldButton->_parent = this;
+ }
+ if (_shieldButton) {
+ _shieldButton->_posX = _shieldButton->_posY = 0;
+ _shieldButton->_width = _gameRef->_renderer->_width;
+ _shieldButton->_height = _gameRef->_renderer->_height;
+
+ _shieldButton->display();
+ }
+ }
+
+ if (!_visible) {
+ return STATUS_OK;
+ }
+
+ if (_fadeBackground) {
+ Graphics::PixelFormat format = _gameRef->_renderer->getPixelFormat();
+ byte fadeR, fadeG, fadeB, fadeA;
+ // First convert from the internal format to the screen-format
+ uint32 fadeColor = format.ARGBToColor(RGBCOLGetA(_fadeColor), RGBCOLGetR(_fadeColor), RGBCOLGetG(_fadeColor), RGBCOLGetB(_fadeColor));
+ // Then get components
+ format.colorToARGB(fadeColor, fadeA, fadeR, fadeG, fadeB);
+ _gameRef->_renderer->fadeToColor(fadeR, fadeG, fadeB, fadeA);
+ }
+
+ if (_dragging) {
+ _posX += (_gameRef->_mousePos.x - _dragFrom.x);
+ _posY += (_gameRef->_mousePos.y - _dragFrom.y);
+
+ _dragFrom.x = _gameRef->_mousePos.x;
+ _dragFrom.y = _gameRef->_mousePos.y;
+ }
+
+ if (!_focusedWidget || (!_focusedWidget->_canFocus || _focusedWidget->_disable || !_focusedWidget->_visible)) {
+ moveFocus();
+ }
+
+ bool popViewport = false;
+ if (_clipContents) {
+ if (!_viewport) {
+ _viewport = new BaseViewport(_gameRef);
+ }
+ if (_viewport) {
+ _viewport->setRect(_posX + offsetX, _posY + offsetY, _posX + _width + offsetX, _posY + _height + offsetY);
+ _gameRef->pushViewport(_viewport);
+ popViewport = true;
+ }
+ }
+
+
+ UITiledImage *back = _back;
+ BaseSprite *image = _image;
+ BaseFont *font = _font;
+
+ if (!isFocused()) {
+ if (_backInactive) {
+ back = _backInactive;
+ }
+ if (_imageInactive) {
+ image = _imageInactive;
+ }
+ if (_fontInactive) {
+ font = _fontInactive;
+ }
+ }
+
+ if (_alphaColor != 0) {
+ _gameRef->_renderer->_forceAlphaColor = _alphaColor;
+ }
+ if (back) {
+ back->display(_posX + offsetX, _posY + offsetY, _width, _height);
+ }
+ if (image) {
+ image->draw(_posX + offsetX, _posY + offsetY, _transparent ? NULL : this);
+ }
+
+ if (!BasePlatform::isRectEmpty(&_titleRect) && font && _text) {
+ font->drawText((byte *)_text, _posX + offsetX + _titleRect.left, _posY + offsetY + _titleRect.top, _titleRect.right - _titleRect.left, _titleAlign, _titleRect.bottom - _titleRect.top);
+ }
+
+ if (!_transparent && !image) {
+ _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, _posX + offsetX, _posY + offsetY, _width, _height, 100, 100, false));
+ }
+
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ _widgets[i]->display(_posX + offsetX, _posY + offsetY);
+ }
+
+ if (_alphaColor != 0) {
+ _gameRef->_renderer->_forceAlphaColor = 0;
+ }
+
+ if (popViewport) {
+ _gameRef->popViewport();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::loadFile(const char *filename) {
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename);
+ if (buffer == NULL) {
+ _gameRef->LOG(0, "UIWindow::LoadFile failed for file '%s'", filename);
+ return STATUS_FAILED;
+ }
+
+ bool ret;
+
+ setFilename(filename);
+
+ if (DID_FAIL(ret = loadBuffer(buffer, true))) {
+ _gameRef->LOG(0, "Error parsing WINDOW file '%s'", filename);
+ }
+
+ delete[] buffer;
+
+ return ret;
+}
+
+
+TOKEN_DEF_START
+TOKEN_DEF(WINDOW)
+TOKEN_DEF(ALPHA_COLOR)
+TOKEN_DEF(ALPHA)
+TOKEN_DEF(TEMPLATE)
+TOKEN_DEF(DISABLED)
+TOKEN_DEF(VISIBLE)
+TOKEN_DEF(BACK_INACTIVE)
+TOKEN_DEF(BACK)
+TOKEN_DEF(IMAGE_INACTIVE)
+TOKEN_DEF(IMAGE)
+TOKEN_DEF(FONT_INACTIVE)
+TOKEN_DEF(FONT)
+TOKEN_DEF(TITLE_ALIGN)
+TOKEN_DEF(TITLE_RECT)
+TOKEN_DEF(TITLE)
+TOKEN_DEF(DRAG_RECT)
+TOKEN_DEF(X)
+TOKEN_DEF(Y)
+TOKEN_DEF(WIDTH)
+TOKEN_DEF(HEIGHT)
+TOKEN_DEF(FADE_ALPHA)
+TOKEN_DEF(FADE_COLOR)
+TOKEN_DEF(CURSOR)
+TOKEN_DEF(NAME)
+TOKEN_DEF(BUTTON)
+TOKEN_DEF(STATIC)
+TOKEN_DEF(TRANSPARENT)
+TOKEN_DEF(SCRIPT)
+TOKEN_DEF(CAPTION)
+TOKEN_DEF(PARENT_NOTIFY)
+TOKEN_DEF(MENU)
+TOKEN_DEF(IN_GAME)
+TOKEN_DEF(CLIP_CONTENTS)
+TOKEN_DEF(PAUSE_MUSIC)
+TOKEN_DEF(EDITOR_PROPERTY)
+TOKEN_DEF(EDIT)
+TOKEN_DEF_END
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::loadBuffer(byte *buffer, bool complete) {
+ TOKEN_TABLE_START(commands)
+ TOKEN_TABLE(WINDOW)
+ TOKEN_TABLE(ALPHA_COLOR)
+ TOKEN_TABLE(ALPHA)
+ TOKEN_TABLE(TEMPLATE)
+ TOKEN_TABLE(DISABLED)
+ TOKEN_TABLE(VISIBLE)
+ TOKEN_TABLE(BACK_INACTIVE)
+ TOKEN_TABLE(BACK)
+ TOKEN_TABLE(IMAGE_INACTIVE)
+ TOKEN_TABLE(IMAGE)
+ TOKEN_TABLE(FONT_INACTIVE)
+ TOKEN_TABLE(FONT)
+ TOKEN_TABLE(TITLE_ALIGN)
+ TOKEN_TABLE(TITLE_RECT)
+ TOKEN_TABLE(TITLE)
+ TOKEN_TABLE(DRAG_RECT)
+ TOKEN_TABLE(X)
+ TOKEN_TABLE(Y)
+ TOKEN_TABLE(WIDTH)
+ TOKEN_TABLE(HEIGHT)
+ TOKEN_TABLE(FADE_ALPHA)
+ TOKEN_TABLE(FADE_COLOR)
+ TOKEN_TABLE(CURSOR)
+ TOKEN_TABLE(NAME)
+ TOKEN_TABLE(BUTTON)
+ TOKEN_TABLE(STATIC)
+ TOKEN_TABLE(TRANSPARENT)
+ TOKEN_TABLE(SCRIPT)
+ TOKEN_TABLE(CAPTION)
+ TOKEN_TABLE(PARENT_NOTIFY)
+ TOKEN_TABLE(MENU)
+ TOKEN_TABLE(IN_GAME)
+ TOKEN_TABLE(CLIP_CONTENTS)
+ TOKEN_TABLE(PAUSE_MUSIC)
+ TOKEN_TABLE(EDITOR_PROPERTY)
+ TOKEN_TABLE(EDIT)
+ TOKEN_TABLE_END
+
+ byte *params;
+ int cmd = 2;
+ BaseParser parser;
+
+ int fadeR = 0, fadeG = 0, fadeB = 0, fadeA = 0;
+ int ar = 0, ag = 0, ab = 0, alpha = 0;
+
+ if (complete) {
+ if (parser.getCommand((char **)&buffer, commands, (char **)&params) != TOKEN_WINDOW) {
+ _gameRef->LOG(0, "'WINDOW' keyword expected.");
+ return STATUS_FAILED;
+ }
+ buffer = params;
+ }
+
+ while (cmd >= PARSERR_TOKENNOTFOUND && (cmd = parser.getCommand((char **)&buffer, commands, (char **)&params)) >= PARSERR_TOKENNOTFOUND) {
+ switch (cmd) {
+ case TOKEN_TEMPLATE:
+ if (DID_FAIL(loadFile((char *)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_NAME:
+ setName((char *)params);
+ break;
+
+ case TOKEN_CAPTION:
+ setCaption((char *)params);
+ break;
+
+ case TOKEN_BACK:
+ delete _back;
+ _back = new UITiledImage(_gameRef);
+ if (!_back || DID_FAIL(_back->loadFile((char *)params))) {
+ delete _back;
+ _back = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_BACK_INACTIVE:
+ delete _backInactive;
+ _backInactive = new UITiledImage(_gameRef);
+ if (!_backInactive || DID_FAIL(_backInactive->loadFile((char *)params))) {
+ delete _backInactive;
+ _backInactive = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE:
+ delete _image;
+ _image = new BaseSprite(_gameRef);
+ if (!_image || DID_FAIL(_image->loadFile((char *)params))) {
+ delete _image;
+ _image = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_IMAGE_INACTIVE:
+ delete _imageInactive,
+ _imageInactive = new BaseSprite(_gameRef);
+ if (!_imageInactive || DID_FAIL(_imageInactive->loadFile((char *)params))) {
+ delete _imageInactive;
+ _imageInactive = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT:
+ if (_font) {
+ _gameRef->_fontStorage->removeFont(_font);
+ }
+ _font = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_font) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_FONT_INACTIVE:
+ if (_fontInactive) {
+ _gameRef->_fontStorage->removeFont(_fontInactive);
+ }
+ _fontInactive = _gameRef->_fontStorage->addFont((char *)params);
+ if (!_fontInactive) {
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_TITLE:
+ setText((char *)params);
+ _gameRef->_stringTable->expand(&_text);
+ break;
+
+ case TOKEN_TITLE_ALIGN:
+ if (scumm_stricmp((char *)params, "left") == 0) {
+ _titleAlign = TAL_LEFT;
+ } else if (scumm_stricmp((char *)params, "right") == 0) {
+ _titleAlign = TAL_RIGHT;
+ } else {
+ _titleAlign = TAL_CENTER;
+ }
+ break;
+
+ case TOKEN_TITLE_RECT:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_titleRect.left, &_titleRect.top, &_titleRect.right, &_titleRect.bottom);
+ break;
+
+ case TOKEN_DRAG_RECT:
+ parser.scanStr((char *)params, "%d,%d,%d,%d", &_dragRect.left, &_dragRect.top, &_dragRect.right, &_dragRect.bottom);
+ break;
+
+ case TOKEN_X:
+ parser.scanStr((char *)params, "%d", &_posX);
+ break;
+
+ case TOKEN_Y:
+ parser.scanStr((char *)params, "%d", &_posY);
+ break;
+
+ case TOKEN_WIDTH:
+ parser.scanStr((char *)params, "%d", &_width);
+ break;
+
+ case TOKEN_HEIGHT:
+ parser.scanStr((char *)params, "%d", &_height);
+ break;
+
+ case TOKEN_CURSOR:
+ delete _cursor;
+ _cursor = new BaseSprite(_gameRef);
+ if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
+ delete _cursor;
+ _cursor = NULL;
+ cmd = PARSERR_GENERIC;
+ }
+ break;
+
+ case TOKEN_BUTTON: {
+ UIButton *btn = new UIButton(_gameRef);
+ if (!btn || DID_FAIL(btn->loadBuffer(params, false))) {
+ delete btn;
+ btn = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ btn->_parent = this;
+ _widgets.add(btn);
+ }
+ }
+ break;
+
+ case TOKEN_STATIC: {
+ UIText *text = new UIText(_gameRef);
+ if (!text || DID_FAIL(text->loadBuffer(params, false))) {
+ delete text;
+ text = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ text->_parent = this;
+ _widgets.add(text);
+ }
+ }
+ break;
+
+ case TOKEN_EDIT: {
+ UIEdit *edit = new UIEdit(_gameRef);
+ if (!edit || DID_FAIL(edit->loadBuffer(params, false))) {
+ delete edit;
+ edit = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ edit->_parent = this;
+ _widgets.add(edit);
+ }
+ }
+ break;
+
+ case TOKEN_WINDOW: {
+ UIWindow *win = new UIWindow(_gameRef);
+ if (!win || DID_FAIL(win->loadBuffer(params, false))) {
+ delete win;
+ win = NULL;
+ cmd = PARSERR_GENERIC;
+ } else {
+ win->_parent = this;
+ _widgets.add(win);
+ }
+ }
+ break;
+
+
+ case TOKEN_TRANSPARENT:
+ parser.scanStr((char *)params, "%b", &_transparent);
+ break;
+
+ case TOKEN_SCRIPT:
+ addScript((char *)params);
+ break;
+
+ case TOKEN_PARENT_NOTIFY:
+ parser.scanStr((char *)params, "%b", &_parentNotify);
+ break;
+
+ case TOKEN_PAUSE_MUSIC:
+ parser.scanStr((char *)params, "%b", &_pauseMusic);
+ break;
+
+ case TOKEN_DISABLED:
+ parser.scanStr((char *)params, "%b", &_disable);
+ break;
+
+ case TOKEN_VISIBLE:
+ parser.scanStr((char *)params, "%b", &_visible);
+ break;
+
+ case TOKEN_MENU:
+ parser.scanStr((char *)params, "%b", &_isMenu);
+ break;
+
+ case TOKEN_IN_GAME:
+ parser.scanStr((char *)params, "%b", &_inGame);
+ break;
+
+ case TOKEN_CLIP_CONTENTS:
+ parser.scanStr((char *)params, "%b", &_clipContents);
+ break;
+
+ case TOKEN_FADE_COLOR:
+ parser.scanStr((char *)params, "%d,%d,%d", &fadeR, &fadeG, &fadeB);
+ _fadeBackground = true;
+ break;
+
+ case TOKEN_FADE_ALPHA:
+ parser.scanStr((char *)params, "%d", &fadeA);
+ _fadeBackground = true;
+ break;
+
+ case TOKEN_EDITOR_PROPERTY:
+ parseEditorProperty(params, false);
+ break;
+
+ case TOKEN_ALPHA_COLOR:
+ parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab);
+ break;
+
+ case TOKEN_ALPHA:
+ parser.scanStr((char *)params, "%d", &alpha);
+ break;
+
+
+ default:
+ if (DID_FAIL(_gameRef->windowLoadHook(this, (char **)&buffer, (char **)params))) {
+ cmd = PARSERR_GENERIC;
+ }
+ }
+ }
+ if (cmd == PARSERR_TOKENNOTFOUND) {
+ _gameRef->LOG(0, "Syntax error in WINDOW definition");
+ return STATUS_FAILED;
+ }
+ if (cmd == PARSERR_GENERIC) {
+ _gameRef->LOG(0, "Error loading WINDOW definition");
+ return STATUS_FAILED;
+ }
+
+ correctSize();
+
+ if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) {
+ ar = ag = ab = 255;
+ }
+ _alphaColor = BYTETORGBA(ar, ag, ab, alpha);
+
+ if (_fadeBackground) {
+ _fadeColor = BYTETORGBA(fadeR, fadeG, fadeB, fadeA);
+ }
+
+ _focusedWidget = NULL;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::saveAsText(BaseDynamicBuffer *buffer, int indent) {
+ buffer->putTextIndent(indent, "WINDOW\n");
+ buffer->putTextIndent(indent, "{\n");
+
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
+ buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ if (_back && _back->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename());
+ }
+ if (_backInactive && _backInactive->getFilename()) {
+ buffer->putTextIndent(indent + 2, "BACK_INACTIVE=\"%s\"\n", _backInactive->getFilename());
+ }
+
+ if (_image && _image->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename());
+ }
+ if (_imageInactive && _imageInactive->getFilename()) {
+ buffer->putTextIndent(indent + 2, "IMAGE_INACTIVE=\"%s\"\n", _imageInactive->getFilename());
+ }
+
+ if (_font && _font->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename());
+ }
+ if (_fontInactive && _fontInactive->getFilename()) {
+ buffer->putTextIndent(indent + 2, "FONT_INACTIVE=\"%s\"\n", _fontInactive->getFilename());
+ }
+
+ if (_cursor && _cursor->getFilename()) {
+ buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename());
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ if (_text) {
+ buffer->putTextIndent(indent + 2, "TITLE=\"%s\"\n", _text);
+ }
+
+ switch (_titleAlign) {
+ case TAL_LEFT:
+ buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "left");
+ break;
+ case TAL_RIGHT:
+ buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "right");
+ break;
+ case TAL_CENTER:
+ buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "center");
+ break;
+ default:
+ error("UIWindow::SaveAsText - Unhandled enum-value NUM_TEXT_ALIGN");
+ }
+
+ if (!BasePlatform::isRectEmpty(&_titleRect)) {
+ buffer->putTextIndent(indent + 2, "TITLE_RECT { %d, %d, %d, %d }\n", _titleRect.left, _titleRect.top, _titleRect.right, _titleRect.bottom);
+ }
+
+ if (!BasePlatform::isRectEmpty(&_dragRect)) {
+ buffer->putTextIndent(indent + 2, "DRAG_RECT { %d, %d, %d, %d }\n", _dragRect.left, _dragRect.top, _dragRect.right, _dragRect.bottom);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ buffer->putTextIndent(indent + 2, "X=%d\n", _posX);
+ buffer->putTextIndent(indent + 2, "Y=%d\n", _posY);
+ buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width);
+ buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height);
+
+ buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE");
+
+ buffer->putTextIndent(indent + 2, "TRANSPARENT=%s\n", _transparent ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "PAUSE_MUSIC=%s\n", _pauseMusic ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "MENU=%s\n", _isMenu ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "IN_GAME=%s\n", _inGame ? "TRUE" : "FALSE");
+ buffer->putTextIndent(indent + 2, "CLIP_CONTENTS=%s\n", _clipContents ? "TRUE" : "FALSE");
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ if (_fadeBackground) {
+ buffer->putTextIndent(indent + 2, "FADE_COLOR { %d, %d, %d }\n", RGBCOLGetR(_fadeColor), RGBCOLGetG(_fadeColor), RGBCOLGetB(_fadeColor));
+ buffer->putTextIndent(indent + 2, "FADE_ALPHA=%d\n", RGBCOLGetA(_fadeColor));
+ }
+
+ buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d, %d, %d }\n", RGBCOLGetR(_alphaColor), RGBCOLGetG(_alphaColor), RGBCOLGetB(_alphaColor));
+ buffer->putTextIndent(indent + 2, "ALPHA=%d\n", RGBCOLGetA(_alphaColor));
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // scripts
+ for (uint32 i = 0; i < _scripts.size(); i++) {
+ buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
+ }
+
+ buffer->putTextIndent(indent + 2, "\n");
+
+ // editor properties
+ BaseClass::saveAsText(buffer, indent + 2);
+
+ // controls
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ _widgets[i]->saveAsText(buffer, indent + 2);
+ }
+
+
+ buffer->putTextIndent(indent, "}\n");
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::enableWidget(const char *name, bool enable) {
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ if (scumm_stricmp(_widgets[i]->getName(), name) == 0) {
+ _widgets[i]->_disable = !enable;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::showWidget(const char *name, bool visible) {
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ if (scumm_stricmp(_widgets[i]->getName(), name) == 0) {
+ _widgets[i]->_visible = visible;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // GetWidget / GetControl
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetWidget") == 0 || strcmp(name, "GetControl") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ if (val->getType() == VAL_INT) {
+ int widget = val->getInt();
+ if (widget < 0 || widget >= (int32)_widgets.size()) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_widgets[widget], true);
+ }
+ } else {
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ if (scumm_stricmp(_widgets[i]->getName(), val->getString()) == 0) {
+ stack->pushNative(_widgets[i], true);
+ return STATUS_OK;
+ }
+ }
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetInactiveFont
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetInactiveFont") == 0) {
+ stack->correctParams(1);
+
+ if (_fontInactive) {
+ _gameRef->_fontStorage->removeFont(_fontInactive);
+ }
+ _fontInactive = _gameRef->_fontStorage->addFont(stack->pop()->getString());
+ stack->pushBool(_fontInactive != NULL);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetInactiveImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetInactiveImage") == 0) {
+ stack->correctParams(1);
+
+ delete _imageInactive;
+ _imageInactive = new BaseSprite(_gameRef);
+ const char *filename = stack->pop()->getString();
+ if (!_imageInactive || DID_FAIL(_imageInactive->loadFile(filename))) {
+ delete _imageInactive;
+ _imageInactive = NULL;
+ stack->pushBool(false);
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetInactiveImage
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetInactiveImage") == 0) {
+ stack->correctParams(0);
+ if (!_imageInactive || !_imageInactive->getFilename()) {
+ stack->pushNULL();
+ } else {
+ stack->pushString(_imageInactive->getFilename());
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetInactiveImageObject
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetInactiveImageObject") == 0) {
+ stack->correctParams(0);
+ if (!_imageInactive) {
+ stack->pushNULL();
+ } else {
+ stack->pushNative(_imageInactive, true);
+ }
+
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Close
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Close") == 0) {
+ stack->correctParams(0);
+ stack->pushBool(DID_SUCCEED(close()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GoExclusive
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GoExclusive") == 0) {
+ stack->correctParams(0);
+ goExclusive();
+ script->waitFor(this);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GoSystemExclusive
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GoSystemExclusive") == 0) {
+ stack->correctParams(0);
+ goSystemExclusive();
+ script->waitFor(this);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Center
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Center") == 0) {
+ stack->correctParams(0);
+ _posX = (_gameRef->_renderer->_width - _width) / 2;
+ _posY = (_gameRef->_renderer->_height - _height) / 2;
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // LoadFromFile
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "LoadFromFile") == 0) {
+ stack->correctParams(1);
+
+ ScValue *val = stack->pop();
+ cleanup();
+ if (!val->isNULL()) {
+ stack->pushBool(DID_SUCCEED(loadFile(val->getString())));
+ } else {
+ stack->pushBool(true);
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateButton
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateButton") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ UIButton *btn = new UIButton(_gameRef);
+ if (!val->isNULL()) {
+ btn->setName(val->getString());
+ }
+ stack->pushNative(btn, true);
+
+ btn->_parent = this;
+ _widgets.add(btn);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateStatic
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateStatic") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ UIText *sta = new UIText(_gameRef);
+ if (!val->isNULL()) {
+ sta->setName(val->getString());
+ }
+ stack->pushNative(sta, true);
+
+ sta->_parent = this;
+ _widgets.add(sta);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateEditor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateEditor") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ UIEdit *edi = new UIEdit(_gameRef);
+ if (!val->isNULL()) {
+ edi->setName(val->getString());
+ }
+ stack->pushNative(edi, true);
+
+ edi->_parent = this;
+ _widgets.add(edi);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // CreateWindow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "CreateWindow") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+
+ UIWindow *win = new UIWindow(_gameRef);
+ if (!val->isNULL()) {
+ win->setName(val->getString());
+ }
+ stack->pushNative(win, true);
+
+ win->_parent = this;
+ _widgets.add(win);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DeleteControl / DeleteButton / DeleteStatic / DeleteEditor / DeleteWindow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DeleteControl") == 0 || strcmp(name, "DeleteButton") == 0 || strcmp(name, "DeleteStatic") == 0 || strcmp(name, "DeleteEditor") == 0 || strcmp(name, "DeleteWindow") == 0) {
+ stack->correctParams(1);
+ ScValue *val = stack->pop();
+ UIObject *obj = (UIObject *)val->getNative();
+
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ if (_widgets[i] == obj) {
+ delete _widgets[i];
+ _widgets.remove_at(i);
+ if (val->getType() == VAL_VARIABLE_REF) {
+ val->setNULL();
+ }
+ }
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ } else if DID_SUCCEED(_gameRef->windowScriptMethodHook(this, script, stack, name)) {
+ return STATUS_OK;
+ }
+
+ else {
+ return UIObject::scCallMethod(script, stack, thisStack, name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+ScValue *UIWindow::scGetProperty(const Common::String &name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (name == "Type") {
+ _scValue->setString("window");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // NumWidgets / NumControls (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "NumWidgets" || name == "NumControls") {
+ _scValue->setInt(_widgets.size());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Exclusive
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Exclusive") {
+ _scValue->setBool(_mode == WINDOW_EXCLUSIVE);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SystemExclusive
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "SystemExclusive") {
+ _scValue->setBool(_mode == WINDOW_SYSTEM_EXCLUSIVE);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Menu
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Menu") {
+ _scValue->setBool(_isMenu);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InGame
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "InGame") {
+ _scValue->setBool(_inGame);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PauseMusic
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "PauseMusic") {
+ _scValue->setBool(_pauseMusic);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ClipContents
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "ClipContents") {
+ _scValue->setBool(_clipContents);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Transparent
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "Transparent") {
+ _scValue->setBool(_transparent);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FadeColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (name == "FadeColor") {
+ _scValue->setInt((int)_fadeColor);
+ return _scValue;
+ } else {
+ return UIObject::scGetProperty(name);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::scSetProperty(const char *name, ScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Name") == 0) {
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Menu
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Menu") == 0) {
+ _isMenu = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // InGame
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "InGame") == 0) {
+ _inGame = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PauseMusic
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PauseMusic") == 0) {
+ _pauseMusic = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ClipContents
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ClipContents") == 0) {
+ _clipContents = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Transparent
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Transparent") == 0) {
+ _transparent = value->getBool();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // FadeColor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FadeColor") == 0) {
+ _fadeColor = (uint32)value->getInt();
+ _fadeBackground = (_fadeColor != 0);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Exclusive
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Exclusive") == 0) {
+ if (value->getBool()) {
+ goExclusive();
+ } else {
+ close();
+ _visible = true;
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SystemExclusive
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SystemExclusive") == 0) {
+ if (value->getBool()) {
+ goSystemExclusive();
+ } else {
+ close();
+ _visible = true;
+ }
+ return STATUS_OK;
+ } else {
+ return UIObject::scSetProperty(name, value);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *UIWindow::scToString() {
+ return "[window]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::handleKeypress(Common::Event *event, bool printable) {
+//TODO
+ if (event->type == Common::EVENT_KEYDOWN && event->kbd.keycode == Common::KEYCODE_TAB) {
+ return DID_SUCCEED(moveFocus(!BaseKeyboardState::isShiftDown()));
+ } else {
+ if (_focusedWidget) {
+ return _focusedWidget->handleKeypress(event, printable);
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::handleMouseWheel(int Delta) {
+ if (_focusedWidget) {
+ return _focusedWidget->handleMouseWheel(Delta);
+ } else {
+ return false;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::handleMouse(TMouseEvent event, TMouseButton button) {
+ bool res = UIObject::handleMouse(event, button);
+
+ // handle window dragging
+ if (!BasePlatform::isRectEmpty(&_dragRect)) {
+ // start drag
+ if (event == MOUSE_CLICK && button == MOUSE_BUTTON_LEFT) {
+ Rect32 dragRect = _dragRect;
+ int offsetX, offsetY;
+ getTotalOffset(&offsetX, &offsetY);
+ dragRect.offsetRect(_posX + offsetX, _posY + offsetY);
+
+ if (BasePlatform::ptInRect(&dragRect, _gameRef->_mousePos)) {
+ _dragFrom.x = _gameRef->_mousePos.x;
+ _dragFrom.y = _gameRef->_mousePos.y;
+ _dragging = true;
+ }
+ }
+ // end drag
+ else if (_dragging && event == MOUSE_RELEASE && button == MOUSE_BUTTON_LEFT) {
+ _dragging = false;
+ }
+ }
+
+ return res;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::persist(BasePersistenceManager *persistMgr) {
+
+ UIObject::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_backInactive));
+ persistMgr->transfer(TMEMBER(_clipContents));
+ persistMgr->transfer(TMEMBER(_dragFrom));
+ persistMgr->transfer(TMEMBER(_dragging));
+ persistMgr->transfer(TMEMBER(_dragRect));
+ persistMgr->transfer(TMEMBER(_fadeBackground));
+ persistMgr->transfer(TMEMBER(_fadeColor));
+ persistMgr->transfer(TMEMBER(_fontInactive));
+ persistMgr->transfer(TMEMBER(_imageInactive));
+ persistMgr->transfer(TMEMBER(_inGame));
+ persistMgr->transfer(TMEMBER(_isMenu));
+ persistMgr->transfer(TMEMBER_INT(_mode));
+ persistMgr->transfer(TMEMBER(_shieldButton));
+ persistMgr->transfer(TMEMBER(_shieldWindow));
+ persistMgr->transfer(TMEMBER_INT(_titleAlign));
+ persistMgr->transfer(TMEMBER(_titleRect));
+ persistMgr->transfer(TMEMBER(_transparent));
+ persistMgr->transfer(TMEMBER(_viewport));
+ persistMgr->transfer(TMEMBER(_pauseMusic));
+
+ _widgets.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::moveFocus(bool forward) {
+ int i;
+ bool found = false;
+ for (i = 0; i < (int32)_widgets.size(); i++) {
+ if (_widgets[i] == _focusedWidget) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ _focusedWidget = NULL;
+ }
+
+ if (!_focusedWidget) {
+ if (_widgets.size() > 0) {
+ i = 0;
+ } else {
+ return STATUS_OK;
+ }
+ }
+
+ int numTries = 0;
+ bool done = false;
+
+ while (numTries <= (int32)_widgets.size()) {
+ if (_widgets[i] != _focusedWidget && _widgets[i]->_canFocus && _widgets[i]->_visible && !_widgets[i]->_disable) {
+ _focusedWidget = _widgets[i];
+ done = true;
+ break;
+ }
+
+ if (forward) {
+ i++;
+ if (i >= (int32)_widgets.size()) {
+ i = 0;
+ }
+ } else {
+ i--;
+ if (i < 0) {
+ i = _widgets.size() - 1;
+ }
+ }
+ numTries++;
+ }
+
+ return done ? STATUS_OK : STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::goExclusive() {
+ if (_mode == WINDOW_EXCLUSIVE) {
+ return STATUS_OK;
+ }
+
+ if (_mode == WINDOW_NORMAL) {
+ _ready = false;
+ _mode = WINDOW_EXCLUSIVE;
+ _visible = true;
+ _disable = false;
+ _gameRef->focusWindow(this);
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::goSystemExclusive() {
+ if (_mode == WINDOW_SYSTEM_EXCLUSIVE) {
+ return STATUS_OK;
+ }
+
+ makeFreezable(false);
+
+ _mode = WINDOW_SYSTEM_EXCLUSIVE;
+ _ready = false;
+ _visible = true;
+ _disable = false;
+ _gameRef->focusWindow(this);
+
+ _gameRef->freeze(_pauseMusic);
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::close() {
+ if (_mode == WINDOW_SYSTEM_EXCLUSIVE) {
+ _gameRef->unfreeze();
+ }
+
+ _mode = WINDOW_NORMAL;
+ _visible = false;
+ _ready = true;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::listen(BaseScriptHolder *param1, uint32 param2) {
+ UIObject *obj = (UIObject *)param1;
+
+ switch (obj->_type) {
+ case UI_BUTTON:
+ if (scumm_stricmp(obj->getName(), "close") == 0) {
+ close();
+ } else {
+ return BaseObject::listen(param1, param2);
+ }
+ break;
+ default:
+ return BaseObject::listen(param1, param2);
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void UIWindow::makeFreezable(bool freezable) {
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ _widgets[i]->makeFreezable(freezable);
+ }
+
+ BaseObject::makeFreezable(freezable);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool UIWindow::getWindowObjects(BaseArray<UIObject *> &objects, bool interactiveOnly) {
+ for (uint32 i = 0; i < _widgets.size(); i++) {
+ UIObject *control = _widgets[i];
+ if (control->_disable && interactiveOnly) {
+ continue;
+ }
+
+ switch (control->_type) {
+ case UI_WINDOW:
+ ((UIWindow *)control)->getWindowObjects(objects, interactiveOnly);
+ break;
+
+ case UI_BUTTON:
+ case UI_EDIT:
+ objects.add(control);
+ break;
+
+ default:
+ if (!interactiveOnly) {
+ objects.add(control);
+ }
+ }
+ }
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/ui/ui_window.h b/engines/wintermute/ui/ui_window.h
new file mode 100644
index 0000000000..ae035c65c7
--- /dev/null
+++ b/engines/wintermute/ui/ui_window.h
@@ -0,0 +1,94 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_UIWINDOW_H
+#define WINTERMUTE_UIWINDOW_H
+
+
+#include "engines/wintermute/ui/ui_object.h"
+#include "common/events.h"
+
+namespace Wintermute {
+
+class UIButton;
+class BaseViewport;
+class UIWindow : public UIObject {
+ uint32 _fadeColor;
+public:
+ bool getWindowObjects(BaseArray<UIObject *> &Objects, bool InteractiveOnly);
+
+ bool _pauseMusic;
+ void cleanup();
+ virtual void makeFreezable(bool freezable);
+ BaseViewport *_viewport;
+ bool _clipContents;
+ bool _inGame;
+ bool _isMenu;
+ bool _fadeBackground;
+
+ virtual bool handleMouseWheel(int delta);
+ UIWindow *_shieldWindow;
+ UIButton *_shieldButton;
+ bool close();
+ bool goSystemExclusive();
+ bool goExclusive();
+ TWindowMode _mode;
+ bool moveFocus(bool forward = true);
+ virtual bool handleMouse(TMouseEvent Event, TMouseButton Button);
+ Point32 _dragFrom;
+ bool _dragging;
+ DECLARE_PERSISTENT(UIWindow, UIObject)
+ bool _transparent;
+ bool showWidget(const char *name, bool visible = true);
+ bool enableWidget(const char *name, bool enable = true);
+ Rect32 _titleRect;
+ Rect32 _dragRect;
+ virtual bool display(int offsetX = 0, int offsetY = 0);
+ UIWindow(BaseGame *inGame);
+ virtual ~UIWindow();
+ virtual bool handleKeypress(Common::Event *event, bool printable = false);
+ BaseArray<UIObject *> _widgets;
+ TTextAlign _titleAlign;
+ bool loadFile(const char *filename);
+ bool loadBuffer(byte *buffer, bool complete = true);
+ UITiledImage *_backInactive;
+ BaseFont *_fontInactive;
+ BaseSprite *_imageInactive;
+ virtual bool listen(BaseScriptHolder *param1, uint32 param2);
+ virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent);
+
+ // scripting interface
+ virtual ScValue *scGetProperty(const Common::String &name);
+ virtual bool scSetProperty(const char *name, ScValue *value);
+ virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name);
+ virtual const char *scToString();
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/utils/convert_utf.cpp b/engines/wintermute/utils/convert_utf.cpp
new file mode 100644
index 0000000000..7ebc011d01
--- /dev/null
+++ b/engines/wintermute/utils/convert_utf.cpp
@@ -0,0 +1,615 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "engines/wintermute/utils/convert_utf.h"
+#ifdef CVTUTF_DEBUG
+#include "common/textconsole.h"
+#endif
+
+namespace Wintermute {
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+#define false 0
+#define true 1
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF16(
+ const UTF32 **sourceStart, const UTF32 *sourceEnd,
+ UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32 *source = *sourceStart;
+ UTF16 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ if (target >= targetEnd) {
+ result = targetExhausted;
+ break;
+ }
+ ch = *source++;
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_LEGAL_UTF32) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ --source; /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF32(
+ const UTF16 **sourceStart, const UTF16 *sourceEnd,
+ UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16 *source = *sourceStart;
+ UTF32 *target = *targetStart;
+ UTF32 ch, ch2;
+ while (source < sourceEnd) {
+ const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ if (target >= targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ *target++ = ch;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+#ifdef CVTUTF_DEBUG
+ if (result == sourceIllegal) {
+ warning("ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
+ }
+#endif
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL
+ };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8(
+ const UTF16 **sourceStart, const UTF16 *sourceEnd,
+ UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16 *source = *sourceStart;
+ UTF8 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ UTF32 ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) {
+ bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) {
+ bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) {
+ bytesToWrite = 3;
+ } else if (ch < (UTF32)0x110000) {
+ bytesToWrite = 4;
+ } else {
+ bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite;
+ result = targetExhausted;
+ break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 3:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 2:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 1:
+ *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source + length;
+ switch (length) {
+ default:
+ return false;
+ /* Everything else falls through when "true"... */
+ case 4:
+ if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3:
+ if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2:
+ if ((a = (*--srcptr)) > 0xBF) return false;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0:
+ if (a < 0xA0) return false;
+ break;
+ case 0xED:
+ if (a > 0x9F) return false;
+ break;
+ case 0xF0:
+ if (a < 0x90) return false;
+ break;
+ case 0xF4:
+ if (a > 0x8F) return false;
+ break;
+ default:
+ if (a < 0x80) return false;
+ }
+
+ case 1:
+ if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+ int length = trailingBytesForUTF8[*source] + 1;
+ if (source + length > sourceEnd) {
+ return false;
+ }
+ return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16(
+ const UTF8 **sourceStart, const UTF8 *sourceEnd,
+ UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8 *source = *sourceStart;
+ UTF16 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted;
+ break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead + 1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5:
+ ch += *source++;
+ ch <<= 6; /* remember, illegal UTF-8 */
+ case 4:
+ ch += *source++;
+ ch <<= 6; /* remember, illegal UTF-8 */
+ case 3:
+ ch += *source++;
+ ch <<= 6;
+ case 2:
+ ch += *source++;
+ ch <<= 6;
+ case 1:
+ ch += *source++;
+ ch <<= 6;
+ case 0:
+ ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead + 1); /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead + 1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead + 1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead + 1); /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF8(
+ const UTF32 **sourceStart, const UTF32 *sourceEnd,
+ UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32 *source = *sourceStart;
+ UTF8 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ ch = *source++;
+ if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /*
+ * Figure out how many bytes the result will require. Turn any
+ * illegally large UTF32 things (> Plane 17) into replacement chars.
+ */
+ if (ch < (UTF32)0x80) {
+ bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) {
+ bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) {
+ bytesToWrite = 3;
+ } else if (ch <= UNI_MAX_LEGAL_UTF32) {
+ bytesToWrite = 4;
+ } else {
+ bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ result = sourceIllegal;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ --source; /* Back up source pointer! */
+ target -= bytesToWrite;
+ result = targetExhausted;
+ break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 3:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 2:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 1:
+ *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF32(
+ const UTF8 **sourceStart, const UTF8 *sourceEnd,
+ UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8 *source = *sourceStart;
+ UTF32 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted;
+ break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead + 1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5:
+ ch += *source++;
+ ch <<= 6;
+ case 4:
+ ch += *source++;
+ ch <<= 6;
+ case 3:
+ ch += *source++;
+ ch <<= 6;
+ case 2:
+ ch += *source++;
+ ch <<= 6;
+ case 1:
+ ch += *source++;
+ ch <<= 6;
+ case 0:
+ ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead + 1); /* Back up the source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ if (ch <= UNI_MAX_LEGAL_UTF32) {
+ /*
+ * UTF-16 surrogate values are illegal in UTF-32, and anything
+ * over Plane 17 (> 0x10FFFF) is illegal.
+ */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead + 1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = ch;
+ }
+ } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
+ result = sourceIllegal;
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
+
+} // End of namespace Wintermute
diff --git a/engines/wintermute/utils/convert_utf.h b/engines/wintermute/utils/convert_utf.h
new file mode 100644
index 0000000000..a5f34456f5
--- /dev/null
+++ b/engines/wintermute/utils/convert_utf.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+// NOTE: Modifications have been made to the code for inclusion
+// into ScummVM.
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Header file.
+
+ Several funtions are included here, forming a complete set of
+ conversions between the three formats. UTF-7 is not included
+ here, but is handled in a separate source file.
+
+ Each of these routines takes pointers to input buffers and output
+ buffers. The input buffers are const.
+
+ Each routine converts the text between *sourceStart and sourceEnd,
+ putting the result into the buffer between *targetStart and
+ targetEnd. Note: the end pointers are *after* the last item: e.g.
+ *(sourceEnd - 1) is the last item.
+
+ The return result indicates whether the conversion was successful,
+ and if not, whether the problem was in the source or target buffers.
+ (Only the first encountered problem is indicated.)
+
+ After the conversion, *sourceStart and *targetStart are both
+ updated to point to the end of last text successfully converted in
+ the respective buffers.
+
+ Input parameters:
+ sourceStart - pointer to a pointer to the source buffer.
+ The contents of this are modified on return so that
+ it points at the next thing to be converted.
+ targetStart - similarly, pointer to pointer to the target buffer.
+ sourceEnd, targetEnd - respectively pointers to the ends of the
+ two buffers, for overflow checking only.
+
+ These conversion functions take a ConversionFlags argument. When this
+ flag is set to strict, both irregular sequences and isolated surrogates
+ will cause an error. When the flag is set to lenient, both irregular
+ sequences and isolated surrogates are converted.
+
+ Whether the flag is strict or lenient, all illegal sequences will cause
+ an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
+ or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
+ must check for illegal sequences.
+
+ When the flag is set to lenient, characters over 0x10FFFF are converted
+ to the replacement character; otherwise (when the flag is set to strict)
+ they constitute an error.
+
+ Output parameters:
+ The value "sourceIllegal" is returned from some routines if the input
+ sequence is malformed. When "sourceIllegal" is returned, the source
+ value will point to the illegal value that caused the problem. E.g.,
+ in UTF-8 when a sequence is malformed, it points to the start of the
+ malformed sequence.
+
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Fixes & updates, Sept 2001.
+
+------------------------------------------------------------------------ */
+
+/* ---------------------------------------------------------------------
+ The following 4 definitions are compiler-specific.
+ The C standard does not guarantee that wchar_t has at least
+ 16 bits, so wchar_t is no less portable than unsigned short!
+ All should be unsigned values to avoid sign extension during
+ bit mask & shift operations.
+------------------------------------------------------------------------ */
+#include "common/system.h"
+
+namespace Wintermute {
+
+typedef uint32 UTF32; /* at least 32 bits */
+typedef uint16 UTF16; /* at least 16 bits */
+typedef uint8 UTF8; /* typically 8 bits */
+typedef uint8 Boolean; /* 0 or 1 */
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+ConversionResult ConvertUTF8toUTF16(
+ const UTF8 **sourceStart, const UTF8 *sourceEnd,
+ UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF8(
+ const UTF16 **sourceStart, const UTF16 *sourceEnd,
+ UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF8toUTF32(
+ const UTF8 **sourceStart, const UTF8 *sourceEnd,
+ UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF32toUTF8(
+ const UTF32 **sourceStart, const UTF32 *sourceEnd,
+ UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF32(
+ const UTF16 **sourceStart, const UTF16 *sourceEnd,
+ UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF32toUTF16(
+ const UTF32 **sourceStart, const UTF32 *sourceEnd,
+ UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags);
+
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
+
+} // End of namespace Wintermute
+
+/* --------------------------------------------------------------------- */
diff --git a/engines/wintermute/utils/crc.cpp b/engines/wintermute/utils/crc.cpp
new file mode 100644
index 0000000000..e7ec45511b
--- /dev/null
+++ b/engines/wintermute/utils/crc.cpp
@@ -0,0 +1,237 @@
+/**********************************************************************
+ *
+ * Filename: crc.c
+ *
+ * Description: Slow and fast implementations of the CRC standards.
+ *
+ * Notes: The parameters for each supported CRC standard are
+ * defined in the header file crc.h. The implementations
+ * here should stand up to further additions to that list.
+ *
+ *
+ * Copyright (c) 2000 by Michael Barr. This software is placed into
+ * the public domain and may be used for any purpose. However, this
+ * notice must not be changed or removed and no warranty is either
+ * expressed or implied by its publication or distribution.
+ **********************************************************************/
+
+#include "engines/wintermute/utils/crc.h"
+
+namespace Wintermute {
+
+/*
+ * Derive parameters from the standard-specific parameters in crc.h.
+ */
+#define WIDTH (8 * sizeof(crc))
+#define TOPBIT (1 << (WIDTH - 1))
+
+#if (REFLECT_DATA == TRUE)
+#undef REFLECT_DATA
+#define REFLECT_DATA(X) ((unsigned char) reflect((X), 8))
+#else
+#undef REFLECT_DATA
+#define REFLECT_DATA(X) (X)
+#endif
+
+#if (REFLECT_REMAINDER == TRUE)
+#undef REFLECT_REMAINDER
+#define REFLECT_REMAINDER(X) ((crc) reflect((X), WIDTH))
+#else
+#undef REFLECT_REMAINDER
+#define REFLECT_REMAINDER(X) (X)
+#endif
+
+
+/*********************************************************************
+ *
+ * Function: reflect()
+ *
+ * Description: Reorder the bits of a binary sequence, by reflecting
+ * them about the middle position.
+ *
+ * Notes: No checking is done that nBits <= 32.
+ *
+ * Returns: The reflection of the original data.
+ *
+ *********************************************************************/
+static unsigned long
+reflect(unsigned long data, unsigned char nBits) {
+ unsigned long reflection = 0x00000000;
+ unsigned char bit;
+
+ /*
+ * Reflect the data about the center bit.
+ */
+ for (bit = 0; bit < nBits; ++bit) {
+ /*
+ * If the LSB bit is set, set the reflection of it.
+ */
+ if (data & 0x01) {
+ reflection |= (1 << ((nBits - 1) - bit));
+ }
+
+ data = (data >> 1);
+ }
+
+ return (reflection);
+
+} /* reflect() */
+
+
+/*********************************************************************
+ *
+ * Function: crcSlow()
+ *
+ * Description: Compute the CRC of a given message.
+ *
+ * Notes:
+ *
+ * Returns: The CRC of the message.
+ *
+ *********************************************************************/
+crc
+crcSlow(unsigned char const message[], int nBytes) {
+ crc remainder = INITIAL_REMAINDER;
+ int byte;
+ unsigned char bit;
+
+
+ /*
+ * Perform modulo-2 division, a byte at a time.
+ */
+ for (byte = 0; byte < nBytes; ++byte) {
+ /*
+ * Bring the next byte into the remainder.
+ */
+ remainder ^= (REFLECT_DATA(message[byte]) << (WIDTH - 8));
+
+ /*
+ * Perform modulo-2 division, a bit at a time.
+ */
+ for (bit = 8; bit > 0; --bit) {
+ /*
+ * Try to divide the current data bit.
+ */
+ if (remainder & TOPBIT) {
+ remainder = (remainder << 1) ^ POLYNOMIAL;
+ } else {
+ remainder = (remainder << 1);
+ }
+ }
+ }
+
+ /*
+ * The final remainder is the CRC result.
+ */
+ return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE);
+
+} /* crcSlow() */
+
+
+crc crcTable[256];
+
+
+/*********************************************************************
+ *
+ * Function: crcInit()
+ *
+ * Description: Populate the partial CRC lookup table.
+ *
+ * Notes: This function must be rerun any time the CRC standard
+ * is changed. If desired, it can be run "offline" and
+ * the table results stored in an embedded system's ROM.
+ *
+ * Returns: None defined.
+ *
+ *********************************************************************/
+void
+crcInit(void) {
+ crc remainder;
+ int dividend;
+ unsigned char bit;
+
+
+ /*
+ * Compute the remainder of each possible dividend.
+ */
+ for (dividend = 0; dividend < 256; ++dividend) {
+ /*
+ * Start with the dividend followed by zeros.
+ */
+ remainder = dividend << (WIDTH - 8);
+
+ /*
+ * Perform modulo-2 division, a bit at a time.
+ */
+ for (bit = 8; bit > 0; --bit) {
+ /*
+ * Try to divide the current data bit.
+ */
+ if (remainder & TOPBIT) {
+ remainder = (remainder << 1) ^ POLYNOMIAL;
+ } else {
+ remainder = (remainder << 1);
+ }
+ }
+
+ /*
+ * Store the result into the table.
+ */
+ crcTable[dividend] = remainder;
+ }
+
+} /* crcInit() */
+
+
+/*********************************************************************
+ *
+ * Function: crcFast()
+ *
+ * Description: Compute the CRC of a given message.
+ *
+ * Notes: crcInit() must be called first.
+ *
+ * Returns: The CRC of the message.
+ *
+ *********************************************************************/
+crc
+crcFast(unsigned char const message[], int nBytes) {
+ crc remainder = INITIAL_REMAINDER;
+ unsigned char data;
+ int byte;
+
+
+ /*
+ * Divide the message by the polynomial, a byte at a time.
+ */
+ for (byte = 0; byte < nBytes; ++byte) {
+ data = (unsigned char)(REFLECT_DATA(message[byte]) ^ (remainder >> (WIDTH - 8)));
+ remainder = crcTable[data] ^ (remainder << 8);
+ }
+
+ /*
+ * The final remainder is the CRC.
+ */
+ return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE);
+
+} /* crcFast() */
+
+
+
+crc crc_initialize(void) {
+ crcInit();
+ return INITIAL_REMAINDER;
+}
+
+crc crc_process_byte(unsigned char byteVal, crc remainder) {
+ unsigned char data;
+ data = (unsigned char)(REFLECT_DATA(byteVal) ^ (remainder >> (WIDTH - 8)));
+ remainder = crcTable[data] ^ (remainder << 8);
+ return remainder;
+}
+
+crc crc_finalize(crc remainder) {
+ return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/utils/crc.h b/engines/wintermute/utils/crc.h
new file mode 100644
index 0000000000..77c2ea267f
--- /dev/null
+++ b/engines/wintermute/utils/crc.h
@@ -0,0 +1,85 @@
+/**********************************************************************
+ *
+ * Filename: crc.h
+ *
+ * Description: A header file describing the various CRC standards.
+ *
+ * Notes:
+ *
+ *
+ * Copyright (c) 2000 by Michael Barr. This software is placed into
+ * the public domain and may be used for any purpose. However, this
+ * notice must not be changed or removed and no warranty is either
+ * expressed or implied by its publication or distribution.
+ **********************************************************************/
+
+#ifndef _crc_h
+#define _crc_h
+
+#include "common/system.h" // For types.
+
+namespace Wintermute {
+
+#ifndef TRUE
+#define FALSE 0
+#define TRUE !FALSE
+#endif
+
+/*
+ * Select the CRC standard from the list that follows.
+ */
+#define CRC32
+
+#if defined(CRC_CCITT)
+
+typedef uint16 crc;
+
+#define CRC_NAME "CRC-CCITT"
+#define POLYNOMIAL 0x1021
+#define INITIAL_REMAINDER 0xFFFF
+#define FINAL_XOR_VALUE 0x0000
+#define REFLECT_DATA FALSE
+#define REFLECT_REMAINDER FALSE
+#define CHECK_VALUE 0x29B1
+
+#elif defined(CRC16)
+
+typedef uint16 crc;
+
+#define CRC_NAME "CRC-16"
+#define POLYNOMIAL 0x8005
+#define INITIAL_REMAINDER 0x0000
+#define FINAL_XOR_VALUE 0x0000
+#define REFLECT_DATA TRUE
+#define REFLECT_REMAINDER TRUE
+#define CHECK_VALUE 0xBB3D
+
+#elif defined(CRC32)
+
+typedef uint32 crc;
+
+#define CRC_NAME "CRC-32"
+#define POLYNOMIAL 0x04C11DB7
+#define INITIAL_REMAINDER 0xFFFFFFFF
+#define FINAL_XOR_VALUE 0xFFFFFFFF
+#define REFLECT_DATA TRUE
+#define REFLECT_REMAINDER TRUE
+#define CHECK_VALUE 0xCBF43926
+
+#else
+
+#error "One of CRC_CCITT, CRC16, or CRC32 must be #define'd."
+
+#endif
+
+void crcInit(void);
+crc crcSlow(unsigned char const message[], int nBytes);
+crc crcFast(unsigned char const message[], int nBytes);
+
+extern "C" crc crc_initialize(void);
+extern "C" crc crc_process_byte(unsigned char byteVal, crc remainder);
+extern "C" crc crc_finalize(crc remainder);
+
+} // End of namespace Wintermute
+
+#endif /* _crc_h */
diff --git a/engines/wintermute/utils/path_util.cpp b/engines/wintermute/utils/path_util.cpp
new file mode 100644
index 0000000000..298f0c268f
--- /dev/null
+++ b/engines/wintermute/utils/path_util.cpp
@@ -0,0 +1,101 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "common/file.h"
+#include "engines/wintermute/utils/path_util.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString PathUtil::unifySeparators(const AnsiString &path) {
+ AnsiString newPath = path;
+
+ for (uint32 i = 0; i < newPath.size(); i++) {
+ if (newPath[i] == '\\') {
+ newPath.setChar('/', i);
+ }
+ }
+
+ return newPath;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString PathUtil::normalizeFileName(const AnsiString &path) {
+ AnsiString newPath = unifySeparators(path);
+ newPath.toLowercase();
+ return newPath;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString PathUtil::combine(const AnsiString &path1, const AnsiString &path2) {
+ AnsiString newPath1 = unifySeparators(path1);
+ AnsiString newPath2 = unifySeparators(path2);
+
+ if (!newPath1.hasSuffix("/") && !newPath2.hasPrefix("/")) {
+ newPath1 += "/";
+ }
+
+ return newPath1 + newPath2;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString PathUtil::getDirectoryName(const AnsiString &path) {
+ AnsiString newPath = unifySeparators(path);
+ Common::String filename = getFileName(path);
+ return Common::String(path.c_str(), path.size() - filename.size());
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString PathUtil::getFileName(const AnsiString &path) {
+ AnsiString newPath = unifySeparators(path);
+ Common::String lastPart = Common::lastPathComponent(newPath, '/');
+ if (lastPart[lastPart.size() - 1 ] != '/') {
+ return lastPart;
+ } else {
+ return path;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString PathUtil::getFileNameWithoutExtension(const AnsiString &path) {
+ AnsiString fileName = getFileName(path);
+ // TODO: Prettify this.
+ AnsiString extension = Common::lastPathComponent(fileName, '.');
+ for (uint32 i = 0; i < extension.size() + 1; i++) {
+ fileName.deleteLastChar();
+ }
+ return fileName;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString PathUtil::getExtension(const AnsiString &path) {
+ AnsiString fileName = getFileName(path);
+ return Common::lastPathComponent(path, '.');
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/utils/path_util.h b/engines/wintermute/utils/path_util.h
new file mode 100644
index 0000000000..7358c2aba0
--- /dev/null
+++ b/engines/wintermute/utils/path_util.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_PATHUTILS_H
+#define WINTERMUTE_PATHUTILS_H
+
+#include "engines/wintermute/dctypes.h"
+
+namespace Wintermute {
+
+class PathUtil {
+public:
+ static AnsiString unifySeparators(const AnsiString &path);
+ static AnsiString normalizeFileName(const AnsiString &path);
+ static AnsiString combine(const AnsiString &path1, const AnsiString &path2);
+ static AnsiString getDirectoryName(const AnsiString &path);
+ static AnsiString getFileName(const AnsiString &path);
+ static AnsiString getFileNameWithoutExtension(const AnsiString &path);
+ static AnsiString getExtension(const AnsiString &path);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/utils/string_util.cpp b/engines/wintermute/utils/string_util.cpp
new file mode 100644
index 0000000000..2c3be8c2f5
--- /dev/null
+++ b/engines/wintermute/utils/string_util.cpp
@@ -0,0 +1,232 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "common/tokenizer.h"
+#include "engines/wintermute/utils/string_util.h"
+#include "engines/wintermute/utils/convert_utf.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+bool StringUtil::compareNoCase(const AnsiString &str1, const AnsiString &str2) {
+ return (str1.compareToIgnoreCase(str2) == 0);
+}
+
+//////////////////////////////////////////////////////////////////////////
+/*bool StringUtil::CompareNoCase(const WideString &str1, const WideString &str2) {
+ WideString str1lc = str1;
+ WideString str2lc = str2;
+
+ ToLowerCase(str1lc);
+ ToLowerCase(str2lc);
+
+ return (str1lc == str2lc);
+}*/
+
+//////////////////////////////////////////////////////////////////////////
+WideString StringUtil::utf8ToWide(const Utf8String &Utf8Str) {
+ error("StringUtil::Utf8ToWide - WideString not supported yet");
+ /* size_t WideSize = Utf8Str.size();
+
+ if (sizeof(wchar_t) == 2) {
+ wchar_t *WideStringNative = new wchar_t[WideSize + 1];
+
+ const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(Utf8Str.c_str());
+ const UTF8 *SourceEnd = SourceStart + WideSize;
+
+ UTF16 *TargetStart = reinterpret_cast<UTF16 *>(WideStringNative);
+ UTF16 *TargetEnd = TargetStart + WideSize + 1;
+
+ ConversionResult res = ConvertUTF8toUTF16(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
+ if (res != conversionOK) {
+ delete[] WideStringNative;
+ return L"";
+ }
+ *TargetStart = 0;
+ WideString ResultString(WideStringNative);
+ delete[] WideStringNative;
+
+ return ResultString;
+ } else if (sizeof(wchar_t) == 4) {
+ wchar_t *WideStringNative = new wchar_t[WideSize + 1];
+
+ const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(Utf8Str.c_str());
+ const UTF8 *SourceEnd = SourceStart + WideSize;
+
+ UTF32 *TargetStart = reinterpret_cast<UTF32 *>(WideStringNative);
+ UTF32 *TargetEnd = TargetStart + WideSize;
+
+ ConversionResult res = ConvertUTF8toUTF32(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
+ if (res != conversionOK) {
+ delete[] WideStringNative;
+ return L"";
+ }
+ *TargetStart = 0;
+ WideString ResultString(WideStringNative);
+ delete[] WideStringNative;
+
+ return ResultString;
+ } else {
+ return L"";
+ }*/
+ return "";
+}
+
+//////////////////////////////////////////////////////////////////////////
+Utf8String StringUtil::wideToUtf8(const WideString &WideStr) {
+ error("StringUtil::wideToUtf8 - Widestring not supported yet");
+ /* size_t WideSize = WideStr.length();
+
+ if (sizeof(wchar_t) == 2) {
+ size_t utf8Size = 3 * WideSize + 1;
+ char *utf8StringNative = new char[Utf8Size];
+
+ const UTF16 *SourceStart = reinterpret_cast<const UTF16 *>(WideStr.c_str());
+ const UTF16 *SourceEnd = SourceStart + WideSize;
+
+ UTF8 *TargetStart = reinterpret_cast<UTF8 *>(Utf8StringNative);
+ UTF8 *TargetEnd = TargetStart + Utf8Size;
+
+ ConversionResult res = ConvertUTF16toUTF8(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
+ if (res != conversionOK) {
+ delete[] Utf8StringNative;
+ return (Utf8String)"";
+ }
+ *TargetStart = 0;
+ Utf8String ResultString(Utf8StringNative);
+ delete[] Utf8StringNative;
+ return ResultString;
+ } else if (sizeof(wchar_t) == 4) {
+ size_t utf8Size = 4 * WideSize + 1;
+ char *utf8StringNative = new char[Utf8Size];
+
+ const UTF32 *SourceStart = reinterpret_cast<const UTF32 *>(WideStr.c_str());
+ const UTF32 *SourceEnd = SourceStart + WideSize;
+
+ UTF8 *TargetStart = reinterpret_cast<UTF8 *>(Utf8StringNative);
+ UTF8 *TargetEnd = TargetStart + Utf8Size;
+
+ ConversionResult res = ConvertUTF32toUTF8(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
+ if (res != conversionOK) {
+ delete[] Utf8StringNative;
+ return (Utf8String)"";
+ }
+ *TargetStart = 0;
+ Utf8String ResultString(Utf8StringNative);
+ delete[] Utf8StringNative;
+ return ResultString;
+ } else {
+ return (Utf8String)"";
+ }*/
+ return "";
+}
+
+// Currently this only does Ansi->ISO 8859, and only for carets.
+char simpleAnsiToWide(const AnsiString &str, uint32 &offset) {
+ char c = str[offset];
+
+ if (c == 92) {
+ offset++;
+ return '\'';
+ } else {
+ offset++;
+ return c;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+WideString StringUtil::ansiToWide(const AnsiString &str) {
+ // TODO: This function gets called a lot, so warnings like these drown out the usefull information
+ /*Common::String converted = "";
+ uint32 index = 0;
+ while (index != str.size()) {
+ converted += simpleAnsiToWide(str, index);
+ }*/
+ // using default os locale!
+
+ /* setlocale(LC_CTYPE, "");
+ size_t wideSize = mbstowcs(NULL, str.c_str(), 0) + 1;
+ wchar_t *wstr = new wchar_t[WideSize];
+ mbstowcs(wstr, str.c_str(), WideSize);
+ WideString ResultString(wstr);
+ delete[] wstr;
+ return ResultString;*/
+ return WideString(str);
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::wideToAnsi(const WideString &wstr) {
+ // using default os locale!
+ // TODO: This function gets called a lot, so warnings like these drown out the usefull information
+ /* setlocale(LC_CTYPE, "");
+ size_t wideSize = wcstombs(NULL, wstr.c_str(), 0) + 1;
+ char *str = new char[WideSize];
+ wcstombs(str, wstr.c_str(), WideSize);
+ AnsiString ResultString(str);
+ delete[] str;
+ return ResultString;*/
+ return AnsiString(wstr);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool StringUtil::isUtf8BOM(const byte *buffer, uint32 bufferSize) {
+ if (bufferSize > 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+int StringUtil::indexOf(const WideString &str, const WideString &toFind, size_t startFrom) {
+ const char *index = strstr(str.c_str(), toFind.c_str());
+ if (index == NULL) {
+ return -1;
+ } else {
+ return index - str.c_str();
+ }
+}
+
+Common::String StringUtil::encodeSetting(const Common::String &str) {
+ if (str.contains('=')) {
+ error("Setting contains '='");
+ }
+ return str;
+}
+
+Common::String StringUtil::decodeSetting(const Common::String &str) {
+ return str;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::toString(int val) {
+ return Common::String::format("%d", val);
+}
+
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/utils/string_util.h b/engines/wintermute/utils/string_util.h
new file mode 100644
index 0000000000..e419e2bca8
--- /dev/null
+++ b/engines/wintermute/utils/string_util.h
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_STRINGUTIL_H
+#define WINTERMUTE_STRINGUTIL_H
+
+#include "engines/wintermute/dctypes.h"
+
+namespace Wintermute {
+
+class StringUtil {
+public:
+ static bool compareNoCase(const AnsiString &str1, const AnsiString &str2);
+ //static bool compareNoCase(const WideString &str1, const WideString &str2);
+ static WideString utf8ToWide(const Utf8String &Utf8Str);
+ static Utf8String wideToUtf8(const WideString &WideStr);
+ static WideString ansiToWide(const AnsiString &str);
+ static AnsiString wideToAnsi(const WideString &str);
+
+ static bool isUtf8BOM(const byte *buffer, uint32 bufferSize);
+ static int indexOf(const WideString &str, const WideString &toFind, size_t startFrom);
+
+ static Common::String encodeSetting(const Common::String &str);
+ static Common::String decodeSetting(const Common::String &str);
+
+ static AnsiString toString(int val);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/utils/utils.cpp b/engines/wintermute/utils/utils.cpp
new file mode 100644
index 0000000000..824b16ccdb
--- /dev/null
+++ b/engines/wintermute/utils/utils.cpp
@@ -0,0 +1,261 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/wintermute.h"
+#include "engines/wintermute/base/base_engine.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+static inline unsigned Sqr(int x) {
+ return (x * x);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// Swap - swaps two integers
+//////////////////////////////////////////////////////////////////////////////////
+void BaseUtils::swap(int *a, int *b) {
+ int temp = *a;
+ *a = *b;
+ *b = temp;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float BaseUtils::normalizeAngle(float angle) {
+ while (angle > 360) {
+ angle -= 360;
+ }
+ while (angle < 0) {
+ angle += 360;
+ }
+
+ return angle;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+void BaseUtils::createPath(const char *path, bool pathOnly) {
+ /* AnsiString pathStr;
+
+ if (!pathOnly) pathStr = PathUtil::getDirectoryName(path);
+ else pathStr = path;
+ */
+// try {
+ warning("BaseUtils::CreatePath - not implemented: %s", path);
+// boost::filesystem::create_directories(path);
+// } catch (...) {
+ return;
+// }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void BaseUtils::debugMessage(const char *text) {
+ //MessageBox(hWnd, Text, "WME", MB_OK|MB_ICONINFORMATION);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *BaseUtils::setString(char **string, const char *value) {
+ delete[] *string;
+ *string = new char[strlen(value) + 1];
+ if (*string) {
+ strcpy(*string, value);
+ }
+ return *string;
+}
+
+//////////////////////////////////////////////////////////////////////////
+char *BaseUtils::strEntry(int entry, const char *str, const char delim) {
+ int numEntries = 0;
+
+ const char *start = NULL;
+ int len = 0;
+
+ for (uint32 i = 0; i <= strlen(str); i++) {
+ if (numEntries == entry) {
+ if (!start) {
+ start = str + i;
+ } else {
+ len++;
+ }
+ }
+ if (str[i] == delim || str[i] == '\0') {
+ numEntries++;
+ if (start) {
+ char *ret = new char[len + 1];
+ memset(ret, 0, len + 1);
+ Common::strlcpy(ret, start, len + 1);
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int BaseUtils::randomInt(int from, int to) {
+ if (to < from) {
+ int i = to;
+ to = from;
+ from = i;
+ }
+ return BaseEngine::instance().randInt(from, to);
+// return (rand() % (to - from + 1)) + from;
+}
+
+//////////////////////////////////////////////////////////////////////////
+float BaseUtils::randomFloat(float from, float to) {
+ const uint32 randMax = RAND_MAX;
+ float randNum = (float)BaseEngine::instance().randInt(0, randMax) / (float)randMax;
+ return from + (to - from) * randNum;
+}
+
+//////////////////////////////////////////////////////////////////////////
+float BaseUtils::randomAngle(float from, float to) {
+ while (to < from) {
+ to += 360;
+ }
+ return normalizeAngle(randomFloat(from, to));
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseUtils::RGBtoHSL(uint32 rgbColor, byte *outH, byte *outS, byte *outL) {
+ float varR = (RGBCOLGetR(rgbColor) / 255.0f);
+ float varG = (RGBCOLGetG(rgbColor) / 255.0f);
+ float varB = (RGBCOLGetB(rgbColor) / 255.0f);
+
+ //Min. value of RGB
+ float varMin = MIN(varR, varG);
+ varMin = MIN(varMin, varB);
+
+ //Max. value of RGB
+ float varMax = MAX(varR, varG);
+ varMax = MAX(varMax, varB);
+
+ //Delta RGB value
+ float delMax = varMax - varMin;
+
+ float H = 0.0f, S = 0.0f, L = 0.0f;
+
+ L = (varMax + varMin) / 2.0f;
+
+ //This is a gray, no chroma...
+ if (delMax == 0) {
+ H = 0;
+ S = 0;
+ }
+ //Chromatic data...
+ else {
+ if (L < 0.5f) {
+ S = delMax / (varMax + varMin);
+ } else {
+ S = delMax / (2.0f - varMax - varMin);
+ }
+
+ float delR = (((varMax - varR) / 6.0f) + (delMax / 2.0f)) / delMax;
+ float delG = (((varMax - varG) / 6.0f) + (delMax / 2.0f)) / delMax;
+ float delB = (((varMax - varB) / 6.0f) + (delMax / 2.0f)) / delMax;
+
+ if (varR == varMax) {
+ H = delB - delG;
+ } else if (varG == varMax) {
+ H = (1.0f / 3.0f) + delR - delB;
+ } else if (varB == varMax) {
+ H = (2.0f / 3.0f) + delG - delR;
+ }
+
+ if (H < 0) {
+ H += 1;
+ }
+ if (H > 1) {
+ H -= 1;
+ }
+ }
+
+ *outH = (byte)(H * 255);
+ *outS = (byte)(S * 255);
+ *outL = (byte)(L * 255);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 BaseUtils::HSLtoRGB(byte InH, byte InS, byte InL) {
+ float H = InH / 255.0f;
+ float S = InS / 255.0f;
+ float L = InL / 255.0f;
+
+ byte R, G, B;
+
+
+ if (S == 0) {
+ R = (byte)(L * 255);
+ G = (byte)(L * 255);
+ B = (byte)(L * 255);
+ } else {
+ float var1, var2;
+
+ if (L < 0.5) {
+ var2 = L * (1.0 + S);
+ } else {
+ var2 = (L + S) - (S * L);
+ }
+
+ var1 = 2.0f * L - var2;
+
+ R = (byte)(255 * Hue2RGB(var1, var2, H + (1.0f / 3.0f)));
+ G = (byte)(255 * Hue2RGB(var1, var2, H));
+ B = (byte)(255 * Hue2RGB(var1, var2, H - (1.0f / 3.0f)));
+ }
+ return BYTETORGBA(255, R, G, B);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float BaseUtils::Hue2RGB(float v1, float v2, float vH) {
+ if (vH < 0.0f) {
+ vH += 1.0f;
+ }
+ if (vH > 1.0f) {
+ vH -= 1.0f;
+ }
+ if ((6.0f * vH) < 1.0f) {
+ return (v1 + (v2 - v1) * 6.0f * vH);
+ }
+ if ((2.0f * vH) < 1.0f) {
+ return (v2);
+ }
+ if ((3.0f * vH) < 2.0f) {
+ return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
+ }
+ return (v1);
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/utils/utils.h b/engines/wintermute/utils/utils.h
new file mode 100644
index 0000000000..d6a603ec72
--- /dev/null
+++ b/engines/wintermute/utils/utils.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_UTILS_H
+#define WINTERMUTE_UTILS_H
+
+#include "engines/wintermute/wintypes.h"
+#include "engines/wintermute/math/rect32.h"
+
+namespace Wintermute {
+
+class BaseGame;
+
+class BaseUtils {
+public:
+ static void swap(int *a, int *b);
+ static float normalizeAngle(float angle);
+
+ static void createPath(const char *path, bool pathOnly = false);
+
+ static void debugMessage(const char *text);
+ static char *setString(char **string, const char *value);
+
+ static char *strEntry(int entry, const char *str, const char delim = ',');
+
+ static int randomInt(int from, int to);
+ static float randomFloat(float from, float to);
+ static float randomAngle(float from, float to);
+
+ static void RGBtoHSL(uint32 rgbColor, byte *outH, byte *outS, byte *outL);
+ static uint32 HSLtoRGB(byte H, byte S, byte L);
+
+private:
+ static float Hue2RGB(float v1, float v2, float vH);
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/video/video_player.cpp b/engines/wintermute/video/video_player.cpp
new file mode 100644
index 0000000000..2577b8aedc
--- /dev/null
+++ b/engines/wintermute/video/video_player.cpp
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+
+#include "engines/wintermute/video/video_player.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+VideoPlayer::VideoPlayer(BaseGame *inGame) : BaseClass(inGame) {
+ setDefaults();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::setDefaults() {
+ _playing = false;
+ _videoEndTime = 0;
+ _soundAvailable = false;
+ _startTime = 0;
+ _totalVideoTime = 0;
+ _playPosX = _playPosY = 0;
+ _playZoom = 0.0f;
+
+ _filename = NULL;
+
+ _slowRendering = false;
+
+ _currentSubtitle = 0;
+ _showSubtitle = false;
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+VideoPlayer::~VideoPlayer() {
+ cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::cleanup() {
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::initialize(const char *inFilename, const char *subtitleFile) {
+ warning("VideoPlayer: %s %s - Not implemented yet", inFilename, subtitleFile);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::update() {
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::display() {
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::play(TVideoPlayback type, int x, int y, bool freezeMusic) {
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::stop() {
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::isPlaying() {
+ return _playing;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoPlayer::loadSubtitles(const char *filename, const char *subtitleFile) {
+ return STATUS_OK;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/video/video_player.h b/engines/wintermute/video/video_player.h
new file mode 100644
index 0000000000..d5466da679
--- /dev/null
+++ b/engines/wintermute/video/video_player.h
@@ -0,0 +1,90 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_VIDPLAYER_H
+#define WINTERMUTE_VIDPLAYER_H
+
+#include "engines/wintermute/dctypes.h" // Added by ClassView
+#include "engines/wintermute/base/base.h"
+
+#define MAX_AUDIO_STREAMS 5
+#define MAX_VIDEO_STREAMS 5
+
+
+namespace Wintermute {
+
+// AVI-Video-player, currently fully stubbed
+class VideoPlayer : public BaseClass {
+public:
+ bool _showSubtitle;
+ int _currentSubtitle;
+ bool loadSubtitles(const char *filename, const char *subtitleFile);
+ bool _slowRendering;
+ bool isPlaying();
+ char *_filename;
+ bool stop();
+ bool play(TVideoPlayback Type = VID_PLAY_CENTER, int x = 0, int y = 0, bool freezeMusic = true);
+ uint32 _totalVideoTime;
+ uint32 _startTime;
+ //CVidRenderer *_vidRenderer;
+ //BaseSoundAVI *_sound;
+ bool _soundAvailable;
+ bool setDefaults();
+ bool _playing;
+ bool display();
+ bool update();
+ bool initialize(const char *inFilename, const char *subtitleFile = NULL);
+ bool cleanup();
+ VideoPlayer(BaseGame *inGame);
+ virtual ~VideoPlayer();
+
+ /*PAVIFILE _aviFile;
+
+ LONG _lastSample;
+
+ PAVISTREAM _audioStream;
+ PAVISTREAM _videoStream;
+
+ LPWAVEFORMAT _audioFormat;
+
+ LPBITMAPINFO _videoFormat;
+ PGETFRAME _videoPGF;*/
+ uint32 _videoEndTime;
+
+ int _playPosX;
+ int _playPosY;
+ float _playZoom;
+
+ /* LPBITMAPV4HEADER _targetFormat;
+
+ BaseArray<CVidSubtitle *, CVidSubtitle *> _subtitles;*/
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/video/video_theora_player.cpp b/engines/wintermute/video/video_theora_player.cpp
new file mode 100644
index 0000000000..0f8bc018ea
--- /dev/null
+++ b/engines/wintermute/video/video_theora_player.cpp
@@ -0,0 +1,505 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+
+#include "engines/wintermute/video/video_theora_player.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "video/theora_decoder.h"
+#include "engines/wintermute/wintermute.h"
+#include "common/system.h"
+
+namespace Wintermute {
+
+IMPLEMENT_PERSISTENT(VideoTheoraPlayer, false)
+
+//////////////////////////////////////////////////////////////////////////
+VideoTheoraPlayer::VideoTheoraPlayer(BaseGame *inGame) : BaseClass(inGame) {
+ SetDefaults();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void VideoTheoraPlayer::SetDefaults() {
+
+ _file = NULL;
+ _filename = "";
+ _startTime = 0;
+ _looping = false;
+
+ _freezeGame = false;
+ _currentTime = 0;
+
+ _state = THEORA_STATE_NONE;
+
+ _videoFrameReady = false;
+ _audioFrameReady = false;
+ _videobufTime = 0;
+
+ _playbackStarted = false;
+ _dontDropFrames = false;
+
+ _texture = NULL;
+ _alphaImage = NULL;
+ _alphaFilename = "";
+
+ _frameRendered = false;
+
+ _seekingKeyframe = false;
+ _timeOffset = 0.0f;
+
+ _posX = _posY = 0;
+ _playbackType = VID_PLAY_CENTER;
+ _playZoom = 0.0f;
+
+ _savedState = THEORA_STATE_NONE;
+ _savedPos = 0;
+ _volume = 100;
+ _theoraDecoder = NULL;
+
+ // TODO: Add subtitles-support
+ //_subtitler = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+VideoTheoraPlayer::~VideoTheoraPlayer(void) {
+ cleanup();
+// SAFE_DELETE(_subtitler);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void VideoTheoraPlayer::cleanup() {
+ if (_file) {
+ BaseFileManager::getEngineInstance()->closeFile(_file);
+ _file = NULL;
+ }
+
+ _surface.free();
+ if (_theoraDecoder) {
+ _theoraDecoder->close();
+ }
+ delete _theoraDecoder;
+ _theoraDecoder = NULL;
+ delete _alphaImage;
+ _alphaImage = NULL;
+ delete _texture;
+ _texture = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::initialize(const Common::String &filename, const Common::String &subtitleFile) {
+ cleanup();
+
+ _filename = filename;
+ _file = BaseFileManager::getEngineInstance()->openFile(filename, true, false);
+ if (!_file) {
+ return STATUS_FAILED;
+ }
+
+#if defined (USE_THEORADEC)
+ _theoraDecoder = new Video::TheoraDecoder();
+#else
+ return STATUS_FAILED;
+#endif
+ _theoraDecoder->loadStream(_file);
+
+ if (!_theoraDecoder->isVideoLoaded()) {
+ return STATUS_FAILED;
+ }
+
+ _state = THEORA_STATE_PAUSED;
+
+ // Additional setup.
+ _surface.create(_theoraDecoder->getWidth(), _theoraDecoder->getHeight(), _theoraDecoder->getPixelFormat());
+ _texture = new BaseSurfaceOSystem(_gameRef);
+ _texture->create(_theoraDecoder->getWidth(), _theoraDecoder->getHeight());
+ _state = THEORA_STATE_PLAYING;
+ _playZoom = 100;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::resetStream() {
+ warning("VidTheoraPlayer::resetStream - stubbed");
+#if 0 // Stubbed for now, as theora isn't seekable
+ if (_sound) {
+ _sound->Stop();
+ }
+
+ m_TimeOffset = 0.0f;
+ Initialize(m_Filename);
+ Play(m_PlaybackType, m_PosX, m_PosY, false, false, m_Looping, 0, m_PlayZoom);
+#endif
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame, bool freezeMusic, bool looping, uint32 startTime, float forceZoom, int volume) {
+ if (forceZoom < 0.0f) {
+ forceZoom = 100.0f;
+ }
+ if (volume < 0) {
+ _volume = _gameRef->_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType);
+ } else {
+ _volume = volume;
+ }
+
+ _freezeGame = freezeGame;
+
+ if (!_playbackStarted && _freezeGame) {
+ _gameRef->freeze(freezeMusic);
+ }
+
+ _playbackStarted = false;
+ float width, height;
+ if (_theoraDecoder) {
+ _surface.free();
+ _surface.copyFrom(*_theoraDecoder->decodeNextFrame());
+ _state = THEORA_STATE_PLAYING;
+ _looping = looping;
+ _playbackType = type;
+
+ _startTime = startTime;
+ _volume = volume;
+ _posX = x;
+ _posY = y;
+ _playZoom = forceZoom;
+
+ width = (float)_theoraDecoder->getWidth();
+ height = (float)_theoraDecoder->getHeight();
+ } else {
+ width = (float)_gameRef->_renderer->_width;
+ height = (float)_gameRef->_renderer->_height;
+ }
+
+ switch (type) {
+ case VID_PLAY_POS:
+ _playZoom = forceZoom;
+ _posX = x;
+ _posY = y;
+ break;
+
+ case VID_PLAY_STRETCH: {
+ float zoomX = (float)((float)_gameRef->_renderer->_width / width * 100);
+ float zoomY = (float)((float)_gameRef->_renderer->_height / height * 100);
+ _playZoom = MIN(zoomX, zoomY);
+ _posX = (int)((_gameRef->_renderer->_width - width * (_playZoom / 100)) / 2);
+ _posY = (int)((_gameRef->_renderer->_height - height * (_playZoom / 100)) / 2);
+ }
+ break;
+
+ case VID_PLAY_CENTER:
+ _playZoom = 100.0f;
+ _posX = (int)((_gameRef->_renderer->_width - width) / 2);
+ _posY = (int)((_gameRef->_renderer->_height - height) / 2);
+ break;
+ }
+ _theoraDecoder->start();
+
+ return STATUS_OK;
+#if 0 // Stubbed for now as theora isn't seekable
+ if (StartTime) SeekToTime(StartTime);
+
+ Update();
+#endif
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::stop() {
+ _theoraDecoder->close();
+ _state = THEORA_STATE_FINISHED;
+ if (_freezeGame) {
+ _gameRef->unfreeze();
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::update() {
+ _currentTime = _freezeGame ? _gameRef->_liveTimer : _gameRef->_timer;
+
+ if (!isPlaying()) {
+ return STATUS_OK;
+ }
+
+ if (_playbackStarted /*&& m_Sound && !m_Sound->IsPlaying()*/) {
+ return STATUS_OK;
+ }
+
+ if (_playbackStarted && !_freezeGame && _gameRef->_state == GAME_FROZEN) {
+ return STATUS_OK;
+ }
+
+ if (_theoraDecoder) {
+ if (_theoraDecoder->endOfVideo() && _looping) {
+ warning("Should loop movie %s", _filename.c_str());
+ _theoraDecoder->rewind();
+ } else if (_theoraDecoder->endOfVideo() && !_looping) {
+ debugC(kWintermuteDebugLog, "Finished movie %s", _filename.c_str());
+ _state = THEORA_STATE_FINISHED;
+ _playbackStarted = false;
+ if (_freezeGame) {
+ _gameRef->unfreeze();
+ }
+ }
+ if (_state == THEORA_STATE_PLAYING) {
+ if (!_theoraDecoder->endOfVideo() && _theoraDecoder->getTimeToNextFrame() == 0) {
+ const Graphics::Surface *decodedFrame = _theoraDecoder->decodeNextFrame();
+ if (decodedFrame) {
+ _surface.free();
+ _surface.copyFrom(*decodedFrame);
+ if (_texture) {
+ writeVideo();
+ }
+ }
+ }
+ return STATUS_OK;
+ }
+ }
+ // Skip the busy-loop?
+ if ((!_texture || !_videoFrameReady) && !_theoraDecoder->endOfVideo()) {
+ // end playback
+ if (!_looping) {
+ _state = THEORA_STATE_FINISHED;
+ if (_freezeGame) {
+ _gameRef->unfreeze();
+ }
+ return STATUS_OK;
+ } else {
+ resetStream();
+ return STATUS_OK;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 VideoTheoraPlayer::getMovieTime() {
+ if (!_playbackStarted) {
+ return 0;
+ } else {
+ return _theoraDecoder->getTime();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::writeVideo() {
+ if (!_texture) {
+ return STATUS_FAILED;
+ }
+
+ _texture->startPixelOp();
+
+ writeAlpha();
+ if (_alphaImage) {
+ _texture->putSurface(_surface, true);
+ } else {
+ _texture->putSurface(_surface, false);
+ }
+
+ //RenderFrame(_texture, &yuv);
+
+ _texture->endPixelOp();
+ _videoFrameReady = true;
+ return STATUS_OK;
+}
+
+void VideoTheoraPlayer::writeAlpha() {
+ if (_alphaImage && _surface.w == _alphaImage->getSurface()->w && _surface.h == _alphaImage->getSurface()->h) {
+ assert(_alphaImage->getSurface()->format.bytesPerPixel == 4);
+ assert(_surface.format.bytesPerPixel == 4);
+ const byte *alphaData = (const byte *)_alphaImage->getSurface()->getBasePtr(0, 0);
+#ifdef SCUMM_LITTLE_ENDIAN
+ int alphaPlace = (_alphaImage->getSurface()->format.aShift / 8);
+#else
+ int alphaPlace = 3 - (_alphaImage->getSurface()->format.aShift / 8);
+#endif
+ alphaData += alphaPlace;
+ byte *imgData = (byte *)_surface.getBasePtr(0, 0);
+#ifdef SCUMM_LITTLE_ENDIAN
+ imgData += (_surface.format.aShift / 8);
+#else
+ imgData += 3 - (_surface.format.aShift / 8);
+#endif
+ for (int i = 0; i < _surface.w * _surface.h; i++) {
+ *imgData = *alphaData;
+ alphaData += 4;
+ imgData += 4;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::display(uint32 alpha) {
+ Rect32 rc;
+ bool res;
+
+ if (_texture && _videoFrameReady) {
+ BasePlatform::setRect(&rc, 0, 0, _texture->getWidth(), _texture->getHeight());
+ if (_playZoom == 100.0f) {
+ res = _texture->displayTrans(_posX, _posY, rc, alpha);
+ } else {
+ res = _texture->displayTransZoom(_posX, _posY, rc, _playZoom, _playZoom, alpha);
+ }
+ } else {
+ res = STATUS_FAILED;
+ }
+ // TODO: Add subtitles-support
+/* if (m_Subtitler && _gameRef->m_VideoSubtitles) {
+ m_Subtitler->display();
+ }*/
+
+ return res;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::setAlphaImage(const Common::String &filename) {
+ delete _alphaImage;
+ _alphaImage = new BaseImage();
+ if (!_alphaImage || DID_FAIL(_alphaImage->loadFile(filename))) {
+ delete _alphaImage;
+ _alphaImage = NULL;
+ _alphaFilename = "";
+ return STATUS_FAILED;
+ }
+
+ if (_alphaFilename != filename) {
+ _alphaFilename = filename;
+ }
+ //TODO: Conversion.
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+byte VideoTheoraPlayer::getAlphaAt(int x, int y) {
+ if (_alphaImage) {
+ return _alphaImage->getAlphaAt(x, y);
+ } else {
+ return 0xFF;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+inline int intlog(int num) {
+ int r = 0;
+ while (num > 0) {
+ num = num / 2;
+ r = r + 1;
+ }
+
+ return r;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::seekToTime(uint32 time) {
+ warning("VideoTheoraPlayer::SeekToTime(%d) - not supported", time);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::pause() {
+ if (_state == THEORA_STATE_PLAYING) {
+ _state = THEORA_STATE_PAUSED;
+ _theoraDecoder->pauseVideo(true);
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::resume() {
+ if (_state == THEORA_STATE_PAUSED) {
+ _state = THEORA_STATE_PLAYING;
+ _theoraDecoder->pauseVideo(false);
+ return STATUS_OK;
+ } else {
+ return STATUS_FAILED;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::persist(BasePersistenceManager *persistMgr) {
+ //BaseClass::persist(persistMgr);
+
+ if (persistMgr->getIsSaving()) {
+ _savedPos = getMovieTime() * 1000;
+ _savedState = _state;
+ } else {
+ SetDefaults();
+ }
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+ persistMgr->transfer(TMEMBER(_savedPos));
+ persistMgr->transfer(TMEMBER(_savedState));
+ persistMgr->transfer(TMEMBER(_filename));
+ persistMgr->transfer(TMEMBER(_alphaFilename));
+ persistMgr->transfer(TMEMBER(_posX));
+ persistMgr->transfer(TMEMBER(_posY));
+ persistMgr->transfer(TMEMBER(_playZoom));
+ persistMgr->transfer(TMEMBER_INT(_playbackType));
+ persistMgr->transfer(TMEMBER(_looping));
+ persistMgr->transfer(TMEMBER(_volume));
+
+ if (!persistMgr->getIsSaving() && (_savedState != THEORA_STATE_NONE)) {
+ initializeSimple();
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool VideoTheoraPlayer::initializeSimple() {
+ if (DID_SUCCEED(initialize(_filename))) {
+ if (_alphaFilename != "") {
+ setAlphaImage(_alphaFilename);
+ }
+ play(_playbackType, _posX, _posY, false, false, _looping, _savedPos, _playZoom);
+ } else {
+ _state = THEORA_STATE_FINISHED;
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BaseSurface *VideoTheoraPlayer::getTexture() {
+ return _texture;
+}
+
+} // end of namespace Wintermute
diff --git a/engines/wintermute/video/video_theora_player.h b/engines/wintermute/video/video_theora_player.h
new file mode 100644
index 0000000000..593c1b9666
--- /dev/null
+++ b/engines/wintermute/video/video_theora_player.h
@@ -0,0 +1,148 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_VIDTHEORAPLAYER_H
+#define WINTERMUTE_VIDTHEORAPLAYER_H
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/persistent.h"
+#include "video/video_decoder.h"
+#include "common/stream.h"
+#include "graphics/surface.h"
+
+namespace Wintermute {
+class BaseSurface;
+class BaseImage;
+class VideoTheoraPlayer : public BaseClass {
+private:
+ enum {
+ THEORA_STATE_NONE = 0,
+ THEORA_STATE_PLAYING = 1,
+ THEORA_STATE_PAUSED = 2,
+ THEORA_STATE_FINISHED = 3
+ };
+ Video::VideoDecoder *_theoraDecoder;
+ Graphics::Surface _surface;
+public:
+ DECLARE_PERSISTENT(VideoTheoraPlayer, BaseClass)
+
+ VideoTheoraPlayer(BaseGame *inGame);
+ virtual ~VideoTheoraPlayer(void);
+
+ // external objects
+ Common::SeekableReadStream *_file;
+ Common::String _filename;
+
+ BaseSurface *_texture;
+ //CVidSubtitler *_subtitler;
+
+ // control methods
+ bool initialize(const Common::String &filename, const Common::String &subtitleFile = NULL);
+ bool initializeSimple();
+ bool update();
+ bool play(TVideoPlayback type = VID_PLAY_CENTER, int x = 0, int y = 0, bool freezeGame = false, bool freezeMusic = true, bool looping = false, uint32 startTime = 0, float forceZoom = -1.0f, int volume = -1);
+ bool stop();
+ bool display(uint32 alpha = 0xFFFFFFFF);
+
+ bool pause();
+ bool resume();
+
+ bool isPlaying() {
+ return _state == THEORA_STATE_PLAYING;
+ };
+ bool isFinished() {
+ return _state == THEORA_STATE_FINISHED;
+ };
+ bool isPaused() {
+ return _state == THEORA_STATE_PAUSED;
+ };
+
+ uint32 getMovieTime();
+
+ BaseSurface *getTexture();
+
+ int _state;
+ uint32 _startTime;
+
+ int _savedState;
+ uint32 _savedPos;
+
+
+ // alpha related
+ BaseImage *_alphaImage;
+ Common::String _alphaFilename;
+ bool setAlphaImage(const Common::String &filename);
+ __inline byte getAlphaAt(int x, int y);
+ void writeAlpha();
+
+ bool seekToTime(uint32 Time);
+
+
+ void cleanup();
+ bool resetStream();
+
+ // video properties
+ TVideoPlayback _playbackType;
+ int _posX;
+ int _posY;
+ float _playZoom;
+ int _volume;
+
+ bool _looping;
+ bool _dontDropFrames;
+ bool _freezeGame;
+ uint32 _currentTime;
+
+
+private:
+ // seeking support
+ bool _seekingKeyframe;
+ float _timeOffset;
+
+ bool _frameRendered;
+
+ bool getIsFrameReady() {
+ return _videoFrameReady;
+ }
+private:
+ bool _audioFrameReady;
+ bool _videoFrameReady;
+ float _videobufTime;
+
+ bool writeVideo();
+
+ bool _playbackStarted;
+
+ // helpers
+ void SetDefaults();
+
+};
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp
new file mode 100644
index 0000000000..c9726e150a
--- /dev/null
+++ b/engines/wintermute/wintermute.cpp
@@ -0,0 +1,383 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/EventRecorder.h"
+#include "common/file.h"
+#include "common/fs.h"
+#include "common/tokenizer.h"
+
+#include "engines/util.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/wintermute.h"
+#include "engines/wintermute/platform_osystem.h"
+#include "engines/wintermute/base/base_engine.h"
+
+#include "engines/wintermute/base/sound/base_sound_manager.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/gfx/base_renderer.h"
+#include "engines/wintermute/base/scriptables/script_engine.h"
+
+namespace Wintermute {
+
+// Simple constructor for detection - we need to setup the persistence to avoid special-casing in-engine
+// This might not be the prettiest solution
+WintermuteEngine::WintermuteEngine() : Engine(g_system) {
+ _game = new AdGame("");
+}
+
+WintermuteEngine::WintermuteEngine(OSystem *syst, const ADGameDescription *desc)
+ : Engine(syst), _gameDescription(desc) {
+ // Put your engine in a sane state, but do nothing big yet;
+ // in particular, do not load data from files; rather, if you
+ // need to do such things, do them from init().
+
+ // Do not initialize graphics here
+
+ // However this is the place to specify all default directories
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ //SearchMan.addSubDirectoryMatching(gameDataDir, "sound");
+
+ // Here is the right place to set up the engine specific debug channels
+ DebugMan.addDebugChannel(kWintermuteDebugLog, "enginelog", "Covers the same output as the log-file in WME");
+ DebugMan.addDebugChannel(kWintermuteDebugSaveGame, "savegame", "Savegames");
+ DebugMan.addDebugChannel(kWintermuteDebugFont, "font", "Text-drawing-related messages");
+ DebugMan.addDebugChannel(kWintermuteDebugFileAccess, "file-access", "Non-critical problems like missing files");
+ DebugMan.addDebugChannel(kWintermuteDebugAudio, "audio", "audio-playback-related issues");
+ DebugMan.addDebugChannel(kWintermuteDebugGeneral, "general", "various issues not covered by any of the above");
+
+ _game = NULL;
+}
+
+WintermuteEngine::~WintermuteEngine() {
+ // Dispose your resources here
+ deinit();
+ delete _game;
+ delete _console;
+
+ // Remove all of our debug levels here
+ DebugMan.clearAllDebugChannels();
+}
+
+bool WintermuteEngine::hasFeature(EngineFeature f) const {
+ switch (f) {
+ case kSupportsRTL:
+ return true;
+ case kSupportsLoadingDuringRuntime:
+ return true;
+ case kSupportsSavingDuringRuntime:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+Common::Error WintermuteEngine::run() {
+ // Initialize graphics using following:
+ Graphics::PixelFormat format(4, 8, 8, 8, 8, 16, 8, 0, 24);
+ initGraphics(800, 600, true, &format);
+ if (g_system->getScreenFormat() != format) {
+ error("Wintermute currently REQUIRES 32bpp");
+ }
+
+ // Create debugger console. It requires GFX to be initialized
+ _console = new Console(this);
+
+// DebugMan.enableDebugChannel("enginelog");
+ debugC(1, kWintermuteDebugLog, "Engine Debug-LOG enabled");
+ debugC(2, kWintermuteDebugSaveGame , "Savegame debugging-enabled");
+
+ int ret = 1;
+
+ // Additional setup.
+ debugC(kWintermuteDebugLog, "WintermuteEngine::init");
+ ret = init();
+
+ debugC(kWintermuteDebugLog, "WintermuteEngine::messageLoop");
+ if (ret == 0) {
+ ret = messageLoop();
+ }
+ deinit();
+ return Common::kNoError;
+}
+
+int WintermuteEngine::init() {
+ BaseEngine::createInstance(_targetName, _gameDescription->language);
+ _game = new AdGame(_targetName);
+ if (!_game) {
+ return 1;
+ }
+ BaseEngine::instance().setGameRef(_game);
+ BasePlatform::initialize(_game, 0, NULL);
+
+ bool windowedMode = !ConfMan.getBool("fullscreen");
+
+ if (ConfMan.hasKey("debug_mode")) {
+ if (ConfMan.getBool("debug_mode")) {
+ _game->DEBUG_DebugEnable("./wme.log");
+ }
+ }
+
+ if (ConfMan.hasKey("show_fps")) {
+ _game->_debugShowFPS = ConfMan.getBool("show_fps");
+ } else {
+ _game->_debugShowFPS = false;
+ }
+
+ if (ConfMan.hasKey("disable_smartcache")) {
+ _game->_smartCache = ConfMan.getBool("disable_smartcache");
+ } else {
+ _game->_smartCache = true;
+ }
+
+ if (!_game->_smartCache) {
+ _game->LOG(0, "Smart cache is DISABLED");
+ }
+
+ // load general game settings
+ _game->initialize1();
+
+ // set gameId, for savegame-naming:
+ _game->setGameId(_targetName);
+
+ if (DID_FAIL(_game->loadSettings("startup.settings"))) {
+ _game->LOG(0, "Error loading game settings.");
+ delete _game;
+ _game = NULL;
+
+ warning("Some of the essential files are missing. Please reinstall.");
+ return 2;
+ }
+
+ _game->initialize2();
+
+ bool ret;
+
+ // initialize the renderer
+ ret = _game->_renderer->initRenderer(_game->_settingsResWidth, _game->_settingsResHeight, windowedMode);
+ if (DID_FAIL(ret)) {
+ _game->LOG(ret, "Error initializing renderer. Exiting.");
+
+ delete _game;
+ _game = NULL;
+ return 3;
+ }
+
+ _game->initialize3();
+
+ // initialize sound manager (non-fatal if we fail)
+ ret = _game->_soundMgr->initialize();
+ if (DID_FAIL(ret)) {
+ _game->LOG(ret, "Sound is NOT available.");
+ }
+
+
+ // load game
+ uint32 dataInitStart = g_system->getMillis();
+
+ if (DID_FAIL(_game->loadFile(_game->_settingsGameFile ? _game->_settingsGameFile : "default.game"))) {
+ _game->LOG(ret, "Error loading game file. Exiting.");
+ delete _game;
+ _game = NULL;
+ return false;
+ }
+
+ _game->_renderer->_ready = true;
+ _game->_miniUpdateEnabled = true;
+
+ _game->LOG(0, "Engine initialized in %d ms", g_system->getMillis() - dataInitStart);
+ _game->LOG(0, "");
+
+ if (ConfMan.hasKey("save_slot")) {
+ int slot = ConfMan.getInt("save_slot");
+ _game->loadGame(slot);
+ }
+
+ // all set, ready to go
+ return 0;
+}
+
+int WintermuteEngine::messageLoop() {
+ bool done = false;
+
+ uint32 prevTime = _system->getMillis();
+ uint32 time = _system->getMillis();
+ uint32 diff = 0;
+
+ const uint32 maxFPS = 60;
+ const uint32 frameTime = (uint32)((1.0 / maxFPS) * 1000);
+ while (!done) {
+ Common::Event event;
+ while (_system->getEventManager()->pollEvent(event)) {
+ BasePlatform::handleEvent(&event);
+ }
+
+ if (_game && _game->_renderer->_active && _game->_renderer->_ready) {
+ _game->displayContent();
+ _game->displayQuickMsg();
+
+ _game->displayDebugInfo();
+
+ time = _system->getMillis();
+ diff = time - prevTime;
+ if (frameTime > diff) { // Avoid overflows
+ _system->delayMillis(frameTime - diff);
+ }
+
+ // ***** flip
+ if (!_game->_suspendedRendering) {
+ _game->_renderer->flip();
+ }
+ if (_game->_loading) {
+ _game->loadGame(_game->_scheduledLoadSlot);
+ }
+ prevTime = time;
+ }
+ if (_game->_quitting) {
+ break;
+ }
+ }
+
+ if (_game) {
+ delete _game;
+ _game = NULL;
+ }
+ return 0;
+}
+
+void WintermuteEngine::deinit() {
+ BaseEngine::destroy();
+}
+
+Common::Error WintermuteEngine::loadGameState(int slot) {
+ BaseEngine::instance().getGameRef()->loadGame(slot);
+ return Common::kNoError;
+}
+
+Common::Error WintermuteEngine::saveGameState(int slot, const Common::String &desc) {
+ BaseEngine::instance().getGameRef()->saveGame(slot, desc.c_str(), false);
+ return Common::kNoError;
+}
+
+bool WintermuteEngine::canSaveGameStateCurrently() {
+ return true;
+}
+
+bool WintermuteEngine::canLoadGameStateCurrently() {
+ return true;
+}
+
+bool WintermuteEngine::getGameInfo(const Common::FSList &fslist, Common::String &name, Common::String &caption) {
+ bool retVal = false;
+ caption = name = "(invalid)";
+ Common::SeekableReadStream *stream = NULL;
+ // Quick-fix, instead of possibly breaking the persistence-system, let's just roll with it
+ BaseFileManager *fileMan = new BaseFileManager(Common::UNK_LANG);
+ fileMan->registerPackages(fslist);
+ stream = fileMan->openFile("startup.settings", false, false);
+
+ // The process is as follows: Check the "GAME=" tag in startup.settings, to decide where the
+ // game-settings are (usually "default.game"), then look into the game-settings to find
+ // the NAME = and CAPTION = tags, to use them to generate a gameid and extras-field
+
+ Common::String settingsGameFile = "default.game";
+ // If the stream-open failed, lets at least attempt to open the default game file afterwards
+ // so, we don't call it a failure yet.
+ if (stream) {
+ while (!stream->eos() && !stream->err()) {
+ Common::String line = stream->readLine();
+ line.trim(); // Get rid of indentation
+ // Expect "SETTINGS {" or comment, or empty line
+ if (line.size() == 0 || line[0] == ';' || (line.contains("{"))) {
+ continue;
+ } else {
+ // We are looking for "GAME ="
+ Common::StringTokenizer token(line, "=");
+ Common::String key = token.nextToken();
+ Common::String value = token.nextToken();
+ if (value.size() == 0) {
+ continue;
+ }
+ if (value[0] == '\"') {
+ value.deleteChar(0);
+ } else {
+ continue;
+ }
+ if (value.lastChar() == '\"') {
+ value.deleteLastChar();
+ }
+ if (key == "GAME") {
+ settingsGameFile = value;
+ break;
+ }
+ }
+ }
+ }
+
+ delete stream;
+ stream = fileMan->openFile(settingsGameFile, false, false);
+ if (stream) {
+ // We do some manual parsing here, as the engine needs gfx to be initalized to do that.
+ while (!stream->eos() && !stream->err()) {
+ Common::String line = stream->readLine();
+ line.trim(); // Get rid of indentation
+ // Expect "GAME {" or comment, or empty line
+ if (line.size() == 0 || line[0] == ';' || (line.contains("{"))) {
+ continue;
+ } else {
+ Common::StringTokenizer token(line, "=");
+ Common::String key = token.nextToken();
+ Common::String value = token.nextToken();
+ if (value.size() == 0) {
+ continue;
+ }
+ if (value[0] == '\"') {
+ value.deleteChar(0);
+ } else {
+ continue; // not a string
+ }
+ if (value.lastChar() == '\"') {
+ value.deleteLastChar();
+ }
+ if (key == "NAME") {
+ retVal = true;
+ name = value;
+ } else if (key == "CAPTION") {
+ retVal = true;
+ caption = value;
+ }
+ }
+ }
+ delete stream;
+ }
+ delete fileMan;
+ BaseEngine::destroy();
+ return retVal;
+}
+
+} // End of namespace Wintermute
diff --git a/engines/wintermute/wintermute.h b/engines/wintermute/wintermute.h
new file mode 100644
index 0000000000..d24b120658
--- /dev/null
+++ b/engines/wintermute/wintermute.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WINTERMUTE_H
+#define WINTERMUTE_H
+
+#include "engines/engine.h"
+#include "engines/advancedDetector.h"
+#include "gui/debugger.h"
+
+namespace Wintermute {
+
+class Console;
+class BaseGame;
+class SystemClassRegistry;
+// our engine debug channels
+enum {
+ kWintermuteDebugLog = 1 << 0, // The debug-logs from the original engine
+ kWintermuteDebugSaveGame = 1 << 1,
+ kWintermuteDebugFont = 1 << 2, // next new channel must be 1 << 2 (4)
+ kWintermuteDebugFileAccess = 1 << 3, // the current limitation is 32 debug channels (1 << 31 is the last one)
+ kWintermuteDebugAudio = 1 << 4,
+ kWintermuteDebugGeneral = 1 << 5
+};
+
+class WintermuteEngine : public Engine {
+public:
+ WintermuteEngine(OSystem *syst, const ADGameDescription *desc);
+ WintermuteEngine();
+ ~WintermuteEngine();
+
+ virtual Common::Error run();
+ virtual bool hasFeature(EngineFeature f) const;
+ Common::SaveFileManager *getSaveFileMan() { return _saveFileMan; }
+ virtual Common::Error loadGameState(int slot);
+ virtual bool canLoadGameStateCurrently();
+ virtual Common::Error saveGameState(int slot, const Common::String &desc);
+ virtual bool canSaveGameStateCurrently();
+ // For detection-purposes:
+ static bool getGameInfo(const Common::FSList &fslist, Common::String &name, Common::String &caption);
+private:
+ int init();
+ void deinit();
+ int messageLoop();
+ Console *_console;
+ BaseGame *_game;
+ const ADGameDescription *_gameDescription;
+};
+
+// Example console class
+class Console : public GUI::Debugger {
+public:
+ Console(WintermuteEngine *vm) {}
+ virtual ~Console(void) {}
+};
+
+} // End of namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/wintypes.h b/engines/wintermute/wintypes.h
new file mode 100644
index 0000000000..c7723808ea
--- /dev/null
+++ b/engines/wintermute/wintypes.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_WINTYPES_H
+#define WINTERMUTE_WINTYPES_H
+
+#include "common/scummsys.h"
+
+namespace Wintermute {
+
+#define BYTETORGBA(r,g,b,a) ((uint32)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
+
+#define RGBCOLGetB(rgb) ((byte )(rgb))
+#define RGBCOLGetG(rgb) ((byte )(((uint16)(rgb)) >> 8))
+#define RGBCOLGetR(rgb) ((byte )((rgb)>>16))
+#define RGBCOLGetA(rgb) ((byte )((rgb)>>24))
+
+#define DID_SUCCEED(hr) ((bool)(hr))
+#define DID_FAIL(hr) (!((bool)(hr)))
+
+#define STATUS_OK (true)
+#define STATUS_FAILED (false)
+
+#define MAX_PATH_LENGTH 512
+
+} // end of namespace Wintermute
+
+#endif
diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp
index 30ef9eeeeb..f426dd8c41 100644
--- a/graphics/VectorRenderer.cpp
+++ b/graphics/VectorRenderer.cpp
@@ -80,7 +80,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect &
case Graphics::DrawStep::kVectorAlignManual:
if (step.x >= 0)
in_x = area.left + step.x + step.padding.left;
- else
+ else
in_x = area.left + area.width() + step.x + step.padding.left; // value relative to the opposite corner.
break;
diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index e98f4aa761..0467cac946 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -55,7 +55,7 @@ struct DrawStep {
bool autoWidth, autoHeight;
int16 x, y, w, h; /**< width, height and position, if not measured automatically.
negative values mean counting from the opposite direction */
-
+
Common::Rect padding;
enum VectorAlignment {
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 1ed5a3308a..6a3ee306a5 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -369,12 +369,12 @@ gradientFill(PixelType *ptr, int width, int x, int y) {
int grad = (((y - _gradIndexes[curGrad]) % stripSize) << 2) / stripSize;
// Dithering:
- // +--+ +--+ +--+ +--+
- // | | | | | *| | *|
- // | | | *| |* | |**|
- // +--+ +--+ +--+ +--+
+ // +--+ +--+ +--+ +--+
+ // | | | | | *| | *|
+ // | | | *| |* | |**|
+ // +--+ +--+ +--+ +--+
// 0 1 2 3
- if (grad == 0 ||
+ if (grad == 0 ||
_gradCache[curGrad] == _gradCache[curGrad + 1] || // no color change
stripSize < 2) { // the stip is small
colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad]);
@@ -873,7 +873,7 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) {
case kTriangleDown:
drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode);
break;
-
+
case kTriangleLeft:
case kTriangleRight:
case kTriangleAuto:
@@ -1206,14 +1206,14 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
pitch = -pitch;
y1 += h;
}
-
+
PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1);
PixelType *floor = ptr_right - 1;
PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + w, y1);
int x2 = x1 + w / 2;
int y2 = y1 + h;
-
+
#if FIXED_POINT
int dx = (x2 - x1) << 8;
int dy = (y2 - y1) << 8;
@@ -1227,7 +1227,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
#endif
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
-
+
#if FIXED_POINT
int gradient = (dy << 8) / dx;
int intery = (y1 << 8) + gradient;
@@ -1250,7 +1250,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_right += pitch;
intery += gradient;
-
+
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@@ -1262,16 +1262,16 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(intery));
break;
case kFillGradient:
- colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
+ colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(intery));
blendPixelPtr(ptr_left, color, rfpart(intery));
break;
}
}
-
+
return;
}
-
+
#if FIXED_POINT
if (abs(dx) < abs(dy)) {
#else
@@ -1280,7 +1280,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_left--;
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
-
+
#if FIXED_POINT
int gradient = (dx << 8) / (dy + 0x100);
int interx = (x1 << 8) + gradient;
@@ -1303,7 +1303,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_right += pitch;
interx += gradient;
-
+
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@@ -1315,18 +1315,18 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
case kFillGradient:
- colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
+ colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(interx));
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
}
}
-
+
return;
}
-
+
ptr_left--;
-
+
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
@@ -1341,12 +1341,12 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
for (int y = y1 + 1; y < y2; y++) {
ptr_right++;
ptr_left--;
-
+
ptr_left += pitch;
ptr_right += pitch;
interx += gradient;
-
+
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@@ -1358,13 +1358,13 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
case kFillGradient:
- colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
+ colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(interx));
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
}
}
-
+
}
/** VERTICAL TRIANGLE DRAWING - FAST VERSION FOR SQUARED TRIANGLES */
@@ -1372,12 +1372,12 @@ template<typename PixelType>
void VectorRendererSpec<PixelType>::
drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) {
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
-
+
if (!inverted) {
pitch = -pitch;
y1 += size;
}
-
+
int gradient_h = 0;
PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1);
PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + size, y1);
@@ -1388,9 +1388,9 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto
int signX = x1 < x2 ? 1 : -1;
int signY = y1 < y2 ? 1 : -1;
int error = deltaX - deltaY;
-
+
colorFill<PixelType>(ptr_right, ptr_left, color);
-
+
while (1) {
switch (fill_m) {
case kFillDisabled:
@@ -1401,22 +1401,22 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto
colorFill<PixelType>(ptr_right, ptr_left, color);
break;
case kFillGradient:
- colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size));
+ colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size));
break;
}
-
+
if (x1 == x2 && y1 == y2)
break;
-
+
int error2 = error * 2;
-
+
if (error2 > -deltaY) {
error -= deltaY;
x1 += signX;
ptr_right += signX;
ptr_left += -signX;
}
-
+
if (error2 < deltaX) {
error += deltaX;
y1 += signY;
diff --git a/graphics/decoders/bmp.cpp b/graphics/decoders/bmp.cpp
index f15d4e2519..bcfd0abbda 100644
--- a/graphics/decoders/bmp.cpp
+++ b/graphics/decoders/bmp.cpp
@@ -100,10 +100,10 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
_paletteColorCount = stream.readUint32LE();
/* uint32 colorsImportant = */ stream.readUint32LE();
- if (_paletteColorCount == 0)
- _paletteColorCount = 256;
-
if (bitsPerPixel == 8) {
+ if (_paletteColorCount == 0)
+ _paletteColorCount = 256;
+
// Read the palette
_palette = new byte[_paletteColorCount * 3];
for (uint16 i = 0; i < _paletteColorCount; i++) {
@@ -155,7 +155,7 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
}
} else { // 32 bpp
byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch;
-
+
for (int32 i = 0; i < height; i++) {
for (uint32 j = 0; j < width; j++) {
byte b = stream.readByte();
@@ -166,11 +166,11 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
// ref: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376%28v=vs.85%29.aspx
stream.readByte();
uint32 color = format.RGBToColor(r, g, b);
-
+
*((uint32 *)dst) = color;
dst += format.bytesPerPixel;
}
-
+
stream.skip(extraDataLength);
dst -= _surface->pitch * 2;
}
diff --git a/graphics/decoders/image_decoder.h b/graphics/decoders/image_decoder.h
index 830645d361..49e31c6e3a 100644
--- a/graphics/decoders/image_decoder.h
+++ b/graphics/decoders/image_decoder.h
@@ -78,10 +78,15 @@ public:
* The palette's format is the same as PaletteManager's palette
* (interleaved RGB values).
*
- * @return the decoded palette, or 0 if no palette is present
+ * @return the decoded palette, or undefined if no palette is present
*/
virtual const byte *getPalette() const { return 0; }
+ /**
+ * Query if the decoded image has a palette.
+ */
+ virtual bool hasPalette() const { return getPaletteColorCount() != 0; }
+
/** Return the starting index of the palette. */
virtual byte getPaletteStartIndex() const { return 0; }
/** Return the number of colors in the palette. */
diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp
index a871377ca1..08bc1f7a3d 100644
--- a/graphics/decoders/jpeg.cpp
+++ b/graphics/decoders/jpeg.cpp
@@ -81,7 +81,7 @@ const Surface *JPEGDecoder::getSurface() const {
const Graphics::Surface *uComponent = getComponent(2);
const Graphics::Surface *vComponent = getComponent(3);
- convertYUV444ToRGB(_rgbSurface, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
+ YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
return _rgbSurface;
}
@@ -452,7 +452,7 @@ bool JPEGDecoder::readSOS() {
_bitsNumber = 0;
for (byte i = 0; i < _numScanComp; i++)
- _scanComp[i]->DCpredictor = 0;
+ _scanComp[i]->DCpredictor = 0;
}
}
}
diff --git a/graphics/decoders/pcx.cpp b/graphics/decoders/pcx.cpp
new file mode 100644
index 0000000000..1250398c73
--- /dev/null
+++ b/graphics/decoders/pcx.cpp
@@ -0,0 +1,213 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+#include "graphics/decoders/pcx.h"
+
+/**
+ * Based on the PCX specs:
+ * http://www.fileformat.info/format/pcx/spec/a10e75307b3a4cc49c3bbe6db4c41fa2/view.htm
+ * and the PCX decoder of FFmpeg (libavcodec/pcx.c):
+ * http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavcodec/pcx.c
+ */
+
+namespace Graphics {
+
+PCXDecoder::PCXDecoder() {
+ _surface = 0;
+ _palette = 0;
+ _paletteColorCount = 0;
+}
+
+PCXDecoder::~PCXDecoder() {
+ destroy();
+}
+
+void PCXDecoder::destroy() {
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ _surface = 0;
+ }
+
+ delete[] _palette;
+ _palette = 0;
+ _paletteColorCount = 0;
+}
+
+bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) {
+ destroy();
+
+ if (stream.readByte() != 0x0a) // ZSoft PCX
+ return false;
+
+ byte version = stream.readByte(); // 0 - 5
+ if (version > 5)
+ return false;
+
+ bool compressed = stream.readByte(); // encoding, 1 = run length encoding
+ byte bitsPerPixel = stream.readByte(); // 1, 2, 4 or 8
+
+ // Window
+ uint16 xMin = stream.readUint16LE();
+ uint16 yMin = stream.readUint16LE();
+ uint16 xMax = stream.readUint16LE();
+ uint16 yMax = stream.readUint16LE();
+
+ uint16 width = xMax - xMin + 1;
+ uint16 height = yMax - yMin + 1;
+
+ if (xMax < xMin || yMax < yMin) {
+ warning("Invalid PCX image dimensions");
+ return false;
+ }
+
+ stream.skip(4); // HDpi, VDpi
+
+ // Read the EGA palette (colormap)
+ _palette = new byte[16 * 3];
+ for (uint16 i = 0; i < 16; i++) {
+ _palette[i * 3 + 0] = stream.readByte();
+ _palette[i * 3 + 1] = stream.readByte();
+ _palette[i * 3 + 2] = stream.readByte();
+ }
+
+ if (stream.readByte() != 0) // reserved, should be set to 0
+ return false;
+
+ byte nPlanes = stream.readByte();
+ uint16 bytesPerLine = stream.readUint16LE();
+ uint16 bytesPerscanLine = nPlanes * bytesPerLine;
+
+ if (bytesPerscanLine < width * bitsPerPixel * nPlanes / 8) {
+ warning("PCX data is corrupted");
+ return false;
+ }
+
+ stream.skip(60); // PaletteInfo, HscreenSize, VscreenSize, Filler
+
+ _surface = new Graphics::Surface();
+
+ byte *scanLine = new byte[bytesPerscanLine];
+ byte *dst;
+ int x, y;
+
+ if (nPlanes == 3 && bitsPerPixel == 8) { // 24bpp
+ Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
+ _surface->create(width, height, format);
+ dst = (byte *)_surface->pixels;
+ _paletteColorCount = 0;
+
+ for (y = 0; y < height; y++) {
+ decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
+
+ for (x = 0; x < width; x++) {
+ byte b = scanLine[x];
+ byte g = scanLine[x + bytesPerLine];
+ byte r = scanLine[x + (bytesPerLine << 1)];
+ uint32 color = format.RGBToColor(r, g, b);
+
+ *((uint32 *)dst) = color;
+ dst += format.bytesPerPixel;
+ }
+ }
+ } else if (nPlanes == 1 && bitsPerPixel == 8) { // 8bpp indexed
+ _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ dst = (byte *)_surface->pixels;
+ _paletteColorCount = 16;
+
+ for (y = 0; y < height; y++, dst += _surface->pitch) {
+ decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
+ memcpy(dst, scanLine, width);
+ }
+
+ if (version == 5) {
+ if (stream.readByte() != 12) {
+ warning("Expected a palette after the PCX image data");
+ delete[] scanLine;
+ return false;
+ }
+
+ // Read the VGA palette
+ delete[] _palette;
+ _palette = new byte[256 * 3];
+ for (uint16 i = 0; i < 256; i++) {
+ _palette[i * 3 + 0] = stream.readByte();
+ _palette[i * 3 + 1] = stream.readByte();
+ _palette[i * 3 + 2] = stream.readByte();
+ }
+
+ _paletteColorCount = 256;
+ }
+ } else if ((nPlanes == 2 || nPlanes == 3 || nPlanes == 4) && bitsPerPixel == 1) { // planar, 4, 8 or 16 colors
+ _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ dst = (byte *)_surface->pixels;
+ _paletteColorCount = 16;
+
+ for (y = 0; y < height; y++, dst += _surface->pitch) {
+ decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
+
+ for (x = 0; x < width; x++) {
+ int m = 0x80 >> (x & 7), v = 0;
+ for (int i = nPlanes - 1; i >= 0; i--) {
+ v <<= 1;
+ v += (scanLine[i * bytesPerLine + (x >> 3)] & m) == 0 ? 0 : 1;
+ }
+ dst[x] = v;
+ }
+ }
+ } else {
+ // Known unsupported case: 1 plane and bpp < 8 (1, 2 or 4)
+ warning("Invalid PCX file (%d planes, %d bpp)", nPlanes, bitsPerPixel);
+ delete[] scanLine;
+ return false;
+ }
+
+ delete[] scanLine;
+
+ return true;
+}
+
+void PCXDecoder::decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerscanLine, bool compressed) {
+ uint32 i = 0;
+ byte run, value;
+
+ if (compressed) {
+ while (i < bytesPerscanLine) {
+ run = 1;
+ value = stream.readByte();
+ if (value >= 0xc0) {
+ run = value & 0x3f;
+ value = stream.readByte();
+ }
+ while (i < bytesPerscanLine && run--)
+ dst[i++] = value;
+ }
+ } else {
+ stream.read(dst, bytesPerscanLine);
+ }
+}
+
+} // End of namespace Graphics
diff --git a/graphics/decoders/pcx.h b/graphics/decoders/pcx.h
new file mode 100644
index 0000000000..b25166b3d9
--- /dev/null
+++ b/graphics/decoders/pcx.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * PCX decoder used in engines:
+ * - dreamweb
+ * - hugo
+ * - queen
+ * - tucker
+ */
+
+#ifndef GRAPHICS_DECODERS_PCX_H
+#define GRAPHICS_DECODERS_PCX_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "graphics/decoders/image_decoder.h"
+
+namespace Common{
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+struct PixelFormat;
+struct Surface;
+
+class PCXDecoder : public ImageDecoder {
+public:
+ PCXDecoder();
+ virtual ~PCXDecoder();
+
+ // ImageDecoder API
+ void destroy();
+ virtual bool loadStream(Common::SeekableReadStream &stream);
+ virtual const Surface *getSurface() const { return _surface; }
+ const byte *getPalette() const { return _palette; }
+ uint16 getPaletteColorCount() const { return _paletteColorCount; }
+
+private:
+ void decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerScanline, bool compressed);
+
+ Surface *_surface;
+ byte *_palette;
+ uint16 _paletteColorCount;
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp
index 7eddd3b893..d35e5c3064 100644
--- a/graphics/decoders/pict.cpp
+++ b/graphics/decoders/pict.cpp
@@ -292,12 +292,12 @@ struct PackBitsRectData {
uint16 mode;
};
-void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette) {
+void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette) {
PackBitsRectData packBitsData;
- packBitsData.pixMap = readPixMap(stream, !hasPalette);
+ packBitsData.pixMap = readPixMap(stream, !withPalette);
// Read in the palette if there is one present
- if (hasPalette) {
+ if (withPalette) {
// See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-267.html
stream.readUint32BE(); // seed
stream.readUint16BE(); // flags
@@ -469,10 +469,10 @@ void PICTDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) {
}
}
-void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette) {
+void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool withPalette) {
// Step through a PackBitsRect/DirectBitsRect function
- if (!hasPalette)
+ if (!withPalette)
stream.readUint32BE();
uint16 rowBytes = stream.readUint16BE();
@@ -492,7 +492,7 @@ void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalet
stream.readUint16BE(); // pixelSize
stream.skip(16);
- if (hasPalette) {
+ if (withPalette) {
stream.readUint32BE();
stream.readUint16BE();
stream.skip((stream.readUint16BE() + 1) * 8);
@@ -543,7 +543,7 @@ void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream)
// Skip the matte and mask
stream.skip(matteSize + maskSize);
-
+
// Now we've reached the image descriptor, so read the relevant data from that
uint32 idStart = stream.pos();
uint32 idSize = stream.readUint32BE();
diff --git a/graphics/decoders/pict.h b/graphics/decoders/pict.h
index 417a7c5134..6f0d86c7a1 100644
--- a/graphics/decoders/pict.h
+++ b/graphics/decoders/pict.h
@@ -24,6 +24,7 @@
* @file
* Image decoder used in engines:
* - mohawk
+ * - pegasus
* - sci
*/
@@ -87,9 +88,9 @@ private:
bool _continueParsing;
// Utility Functions
- void unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette);
+ void unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette);
void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *stream, byte bitsPerPixel, byte bytesPerPixel);
- void skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette);
+ void skipBitsRect(Common::SeekableReadStream &stream, bool withPalette);
void decodeCompressedQuickTime(Common::SeekableReadStream &stream);
void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel);
diff --git a/graphics/decoders/png.cpp b/graphics/decoders/png.cpp
index bfaab6dc35..4f917b44b1 100644
--- a/graphics/decoders/png.cpp
+++ b/graphics/decoders/png.cpp
@@ -191,6 +191,7 @@ bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
}
// After the transformations have been registered, the image data is read again.
+ png_set_interlace_handling(pngPtr);
png_read_update_info(pngPtr, infoPtr);
png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL);
width = w;
diff --git a/graphics/decoders/tga.cpp b/graphics/decoders/tga.cpp
new file mode 100644
index 0000000000..c3b9d84055
--- /dev/null
+++ b/graphics/decoders/tga.cpp
@@ -0,0 +1,427 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* Based on code from xoreos https://github.com/DrMcCoy/xoreos/
+ * relicensed under GPLv2+ with permission from DrMcCoy and clone2727
+ */
+
+#include "common/util.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+#include "common/error.h"
+
+#include "graphics/decoders/tga.h"
+
+namespace Graphics {
+
+TGADecoder::TGADecoder() {
+ _colorMapSize = 0;
+ _colorMapOrigin = 0;
+ _colorMapLength = 0;
+ _colorMapEntryLength = 0;
+ _colorMap = NULL;
+}
+
+TGADecoder::~TGADecoder() {
+ destroy();
+}
+
+void TGADecoder::destroy() {
+ _surface.free();
+ delete[] _colorMap;
+}
+
+bool TGADecoder::loadStream(Common::SeekableReadStream &tga) {
+ byte imageType, pixelDepth;
+ bool success;
+ success = readHeader(tga, imageType, pixelDepth);
+ if (success) {
+ switch (imageType) {
+ case TYPE_BW:
+ case TYPE_TRUECOLOR:
+ success = readData(tga, imageType, pixelDepth);
+ break;
+ case TYPE_RLE_BW:
+ case TYPE_RLE_TRUECOLOR:
+ case TYPE_RLE_CMAP:
+ success = readDataRLE(tga, imageType, pixelDepth);
+ break;
+ case TYPE_CMAP:
+ success = readDataColorMapped(tga, imageType, pixelDepth);
+ break;
+ default:
+ success = false;
+ break;
+ }
+ }
+ if (tga.err() || !success) {
+ warning("Failed reading TGA-file");
+ return false;
+ }
+ return success;
+}
+
+bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) {
+ if (!tga.seek(0)) {
+ warning("Failed reading TGA-file");
+ return false;
+ }
+
+ // TGAs have an optional "id" string in the header
+ uint32 idLength = tga.readByte();
+
+ // Number of colors in the color map / palette
+ int hasColorMap = tga.readByte();
+
+ // Image type. See header for numeric constants
+ imageType = tga.readByte();
+
+ switch (imageType) {
+ case TYPE_CMAP:
+ case TYPE_TRUECOLOR:
+ case TYPE_BW:
+ case TYPE_RLE_CMAP:
+ case TYPE_RLE_TRUECOLOR:
+ case TYPE_RLE_BW:
+ break;
+ default:
+ warning("Unsupported image type: %d", imageType);
+ return false;
+ }
+
+ // Color map specifications
+ if (hasColorMap == 0) {
+ tga.skip(5);
+ } else {
+ _colorMapOrigin = tga.readUint16LE();
+ _colorMapLength = tga.readUint16LE();
+ _colorMapEntryLength = tga.readByte();
+ }
+ // Origin-defintions
+ tga.skip(2 + 2);
+
+ // Image dimensions
+ _surface.w = tga.readUint16LE();
+ _surface.h = tga.readUint16LE();
+
+ // Bits per pixel
+ pixelDepth = tga.readByte();
+ _surface.format.bytesPerPixel = pixelDepth / 8;
+
+ // Image descriptor
+ byte imgDesc = tga.readByte();
+ int attributeBits = imgDesc & 0x0F;
+ assert((imgDesc & 0x10) == 0);
+ _originTop = (imgDesc & 0x20);
+
+ // Interleaving is not handled at this point
+ //int interleave = (imgDesc & 0xC);
+ if (imageType == TYPE_CMAP || imageType == TYPE_RLE_CMAP) {
+ if (pixelDepth == 8) {
+ _format = PixelFormat::createFormatCLUT8();
+ } else {
+ warning("Unsupported index-depth: %d", pixelDepth);
+ return false;
+ }
+ } else if (imageType == TYPE_TRUECOLOR || imageType == TYPE_RLE_TRUECOLOR) {
+ if (pixelDepth == 24) {
+ _format = PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
+ } else if (pixelDepth == 32) {
+ // HACK: According to the spec, attributeBits should determine the amount
+ // of alpha-bits, however, as the game files that use this decoder seems
+ // to ignore that fact, we force the amount to 8 for 32bpp files for now.
+ _format = PixelFormat(4, 8, 8, 8, /* attributeBits */ 8, 16, 8, 0, 24);
+ } else if (pixelDepth == 16 && imageType == TYPE_TRUECOLOR) {
+ // 16bpp TGA is ARGB1555
+ _format = PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15);
+ } else {
+ warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
+ return false;
+ }
+ } else if (imageType == TYPE_BW || TYPE_RLE_BW) {
+ if (pixelDepth == 8) {
+ _format = PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 0);
+ } else {
+ warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
+ return false;
+ }
+
+ } else {
+ warning("Unsupported image type: %d", imageType);
+ return false;
+ }
+
+ // Skip the id string
+ tga.skip(idLength);
+
+ if (hasColorMap) {
+ return readColorMap(tga, imageType, pixelDepth);
+ }
+ return true;
+}
+
+bool TGADecoder::readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
+ _colorMap = new byte[3 * _colorMapLength];
+ for (int i = 0; i < _colorMapLength * 3; i += 3) {
+ byte r, g, b;
+ if (_colorMapEntryLength == 32) {
+ byte a;
+ PixelFormat format(4, 8, 8, 8, 0, 16, 8, 0, 24);
+ uint32 color = tga.readUint32LE();
+ format.colorToARGB(color, a, r, g, b);
+ } else if (_colorMapEntryLength == 24) {
+ r = tga.readByte();
+ g = tga.readByte();
+ b = tga.readByte();
+ } else if (_colorMapEntryLength == 16) {
+ byte a;
+ PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 15);
+ uint16 color = tga.readUint16LE();
+ format.colorToARGB(color, a, r, g, b);
+ } else {
+ warning("Unsupported image type: %d", imageType);
+ r = g = b = 0;
+ }
+#ifdef SCUMM_LITTLE_ENDIAN
+ _colorMap[i] = r;
+ _colorMap[i + 1] = g;
+ _colorMap[i + 2] = b;
+#else
+ _colorMap[i] = b;
+ _colorMap[i + 1] = g;
+ _colorMap[i + 2] = r;
+#endif
+ }
+ return true;
+}
+
+// Additional information found from http://paulbourke.net/dataformats/tga/
+// With some details from the link referenced in the header.
+bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
+ // TrueColor
+ if (imageType == TYPE_TRUECOLOR) {
+ _surface.create(_surface.w, _surface.h, _format);
+
+ if (pixelDepth == 16) {
+ for (int i = 0; i < _surface.h; i++) {
+ uint16 *dst;
+ if (!_originTop) {
+ dst = (uint16 *)_surface.getBasePtr(0, _surface.h - i - 1);
+ } else {
+ dst = (uint16 *)_surface.getBasePtr(0, i);
+ }
+ for (int j = 0; j < _surface.w; j++) {
+ *dst++ = tga.readUint16LE();
+ }
+ }
+ } else if (pixelDepth == 32) {
+ for (int i = 0; i < _surface.h; i++) {
+ uint32 *dst;
+ if (!_originTop) {
+ dst = (uint32 *)_surface.getBasePtr(0, _surface.h - i - 1);
+ } else {
+ dst = (uint32 *)_surface.getBasePtr(0, i);
+ }
+ for (int j = 0; j < _surface.w; j++) {
+ *dst++ = tga.readUint32LE();
+ }
+ }
+ } else if (pixelDepth == 24) {
+ for (int i = 0; i < _surface.h; i++) {
+ byte *dst;
+ if (!_originTop) {
+ dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1);
+ } else {
+ dst = (byte *)_surface.getBasePtr(0, i);
+ }
+ for (int j = 0; j < _surface.w; j++) {
+ byte r = tga.readByte();
+ byte g = tga.readByte();
+ byte b = tga.readByte();
+#ifdef SCUMM_LITTLE_ENDIAN
+ *dst++ = r;
+ *dst++ = g;
+ *dst++ = b;
+#else
+ *dst++ = b;
+ *dst++ = g;
+ *dst++ = r;
+#endif
+ }
+ }
+ }
+ // Black/White
+ } else if (imageType == TYPE_BW) {
+ _surface.create(_surface.w, _surface.h, _format);
+
+ byte *data = (byte *)_surface.pixels;
+ uint32 count = _surface.w * _surface.h;
+
+ while (count-- > 0) {
+ byte g = tga.readByte();
+ *data++ = g;
+ *data++ = g;
+ *data++ = g;
+ *data++ = g;
+ }
+ }
+ return true;
+}
+
+bool TGADecoder::readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth) {
+ // Color-mapped
+ if (imageType == TYPE_CMAP) {
+ _surface.create(_surface.w, _surface.h, _format);
+ if (indexDepth == 8) {
+ for (int i = 0; i < _surface.h; i++) {
+ byte *dst;
+ if (!_originTop) {
+ dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1);
+ } else {
+ dst = (byte *)_surface.getBasePtr(0, i);
+ }
+ for (int j = 0; j < _surface.w; j++) {
+ byte index = tga.readByte();
+ *dst++ = index;
+ }
+ }
+ } else if (indexDepth == 16) {
+ warning("16 bit indexes not supported");
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
+ // RLE-TrueColor / RLE-Black/White
+ if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) {
+ _surface.create(_surface.w, _surface.h, _format);
+ uint32 count = _surface.w * _surface.h;
+ byte *data = (byte *)_surface.pixels;
+
+ while (count > 0) {
+ uint32 header = tga.readByte();
+ byte type = (header & 0x80) >> 7;
+ uint32 rleCount = (header & 0x7F) + 1;
+
+ // RLE-packet
+ if (type == 1) {
+ if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) {
+ uint32 color = tga.readUint32LE();
+ while (rleCount-- > 0) {
+ *((uint32 *)data) = color;
+ data += 4;
+ count--;
+ }
+ } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) {
+ byte r = tga.readByte();
+ byte g = tga.readByte();
+ byte b = tga.readByte();
+ while (rleCount-- > 0) {
+#ifdef SCUMM_LITTLE_ENDIAN
+ *data++ = r;
+ *data++ = g;
+ *data++ = b;
+#else
+ *data++ = b;
+ *data++ = g;
+ *data++ = r;
+#endif
+ count--;
+ }
+ } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) {
+ byte color = tga.readByte();
+ while (rleCount-- > 0) {
+ *data++ = color;
+ *data++ = color;
+ *data++ = color;
+ *data++ = color;
+ count--;
+ }
+ } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) {
+ byte index = tga.readByte();
+ while (rleCount-- > 0) {
+ *data++ = index;
+ count--;
+ }
+ } else {
+ warning("Unhandled pixel-depth for image-type 10");
+ return false;
+ }
+ // Raw-packet
+ } else if (type == 0) {
+ if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) {
+ while (rleCount-- > 0) {
+ uint32 color = tga.readUint32LE();
+ *((uint32 *)data) = color;
+ data += 4;
+ count--;
+ }
+ } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) {
+ while (rleCount-- > 0) {
+ byte r = tga.readByte();
+ byte g = tga.readByte();
+ byte b = tga.readByte();
+#ifdef SCUMM_LITTLE_ENDIAN
+ *data++ = r;
+ *data++ = g;
+ *data++ = b;
+#else
+ *data++ = b;
+ *data++ = g;
+ *data++ = r;
+#endif
+ count--;
+ }
+ } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) {
+ while (rleCount-- > 0) {
+ byte color = tga.readByte();
+ *data++ = color;
+ *data++ = color;
+ *data++ = color;
+ *data++ = color;
+ count--;
+ }
+ } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) {
+ while (rleCount-- > 0) {
+ byte index = tga.readByte();
+ *data++ = index;
+ count--;
+ }
+ } else {
+ warning("Unhandled pixel-depth for image-type 10");
+ return false;
+ }
+ } else {
+ warning("Unknown header for RLE-packet %d", type);
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/decoders/tga.h b/graphics/decoders/tga.h
new file mode 100644
index 0000000000..dfdc5a4da9
--- /dev/null
+++ b/graphics/decoders/tga.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* Based on code from eos https://github.com/DrMcCoy/xoreos/
+ * relicensed under GPLv2+ with permission from DrMcCoy and clone2727
+ */
+
+/*
+ * TGA decoder used in engines:
+ * - none
+ */
+
+#ifndef GRAPHICS_DECODERS_TGA_H
+#define GRAPHICS_DECODERS_TGA_H
+
+#include "graphics/surface.h"
+#include "graphics/decoders/image_decoder.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+/** TarGa image-decoder
+ * The following variations of TGA are supported:
+ * - Type 1 - Color-mapped images in 16/24/32 bpp with 8 bit indexes
+ * - Type 2 - 16/24/32 bpp Top AND Bottom origined.
+ * - Type 3 - Black/White images, 8bpp.
+ * - Type 9 - RLE-encoded color-mapped images. (8 bit indexes only)
+ * - Type 10 - RLE-encoded TrueColor, 24/32bpp.
+ * - Type 11 - RLE-encoded Black/White, 8bpp.
+ *
+ * No images are returned with a palette, instead they are converted
+ * to 16 bpp for Type 1, or 32 bpp for Black/White-images.
+ */
+class TGADecoder : public ImageDecoder {
+public:
+ TGADecoder();
+ virtual ~TGADecoder();
+ virtual void destroy();
+ virtual const Surface *getSurface() const { return &_surface; }
+ virtual const byte *getPalette() const { return _colorMap; }
+ virtual uint16 getPaletteColorCount() const { return _colorMapLength; }
+ virtual bool loadStream(Common::SeekableReadStream &stream);
+private:
+ // Format-spec from:
+ //http://www.ludorg.net/amnesia/TGA_File_Format_Spec.html
+ enum {
+ TYPE_CMAP = 1,
+ TYPE_TRUECOLOR = 2,
+ TYPE_BW = 3,
+ TYPE_RLE_CMAP = 9,
+ TYPE_RLE_TRUECOLOR = 10,
+ TYPE_RLE_BW = 11
+ };
+
+ // Color-map:
+ bool _colorMapSize;
+ byte *_colorMap;
+ int16 _colorMapOrigin;
+ int16 _colorMapLength;
+ byte _colorMapEntryLength;
+
+ // Origin may be at the top, or bottom
+ bool _originTop;
+
+ PixelFormat _format;
+ Surface _surface;
+ // Loading helpers
+ bool readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth);
+ bool readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
+ bool readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth);
+ bool readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
+ bool readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
+};
+
+} // End of namespace Graphics
+
+#endif // GRAPHICS_DECODERS_TGA_H
diff --git a/graphics/fonts/consolefont.cpp b/graphics/fonts/consolefont.cpp
index 8244d75fc2..748aa08a5c 100644
--- a/graphics/fonts/consolefont.cpp
+++ b/graphics/fonts/consolefont.cpp
@@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:32:21 2012
#include "graphics/fonts/bdf.h"
-// Font information:
+// Font information:
// Name: -Misc-Fixed-Medium-R-Normal--8-80-75-75-C-50-ISO8859-1
// Size: 5x8
// Box: 5 8 0 -1
diff --git a/graphics/fonts/newfont.cpp b/graphics/fonts/newfont.cpp
index 10af1efb0c..4922e24676 100644
--- a/graphics/fonts/newfont.cpp
+++ b/graphics/fonts/newfont.cpp
@@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:33:07 2012
#include "graphics/fonts/bdf.h"
-// Font information:
+// Font information:
// Name: -Schumacher-Clean-Medium-R-Normal--12-120-75-75-C-60-ISO8859-1
// Size: 6x12
// Box: 6 12 0 -3
diff --git a/graphics/fonts/newfont_big.cpp b/graphics/fonts/newfont_big.cpp
index 0e61068ade..550d6dbfa9 100644
--- a/graphics/fonts/newfont_big.cpp
+++ b/graphics/fonts/newfont_big.cpp
@@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:33:14 2012
#include "graphics/fonts/bdf.h"
-// Font information:
+// Font information:
// Name: -Adobe-Helvetica-Bold-R-Normal--12-120-75-75-P-70-ISO8859-1
// Size: 13x14
// Box: 13 15 -1 -3
diff --git a/graphics/module.mk b/graphics/module.mk
index 281f904b38..f560d9dc97 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -25,8 +25,10 @@ MODULE_OBJS := \
yuv_to_rgb.o \
decoders/bmp.o \
decoders/jpeg.o \
+ decoders/pcx.o \
decoders/pict.o \
- decoders/png.o
+ decoders/png.o \
+ decoders/tga.o
ifdef USE_SCALERS
MODULE_OBJS += \
diff --git a/graphics/primitives.cpp b/graphics/primitives.cpp
index 9834af65ba..b88db39f36 100644
--- a/graphics/primitives.cpp
+++ b/graphics/primitives.cpp
@@ -61,59 +61,21 @@ void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, i
}
}
+void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data) {
+ assert(penX > 0 && penY > 0);
-// FIXME: This is a limited version of thick line drawing
-// it draws striped lines at some angles. Better algorithm could
-// be found here:
-//
-// http://homepages.enterprise.net/murphy/thickline/index.html
-//
-// Feel free to replace it with better implementation
-void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data) {
- const bool steep = ABS(y1 - y0) > ABS(x1 - x0);
-
- if (steep) {
- SWAP(x0, y0);
- SWAP(x1, y1);
- }
-
- float dx = x1 - x0;
- float dy = y1 - y0;
- float d = (float)sqrt(dx * dx + dy * dy);
-
- if (!d)
+ // Shortcut
+ if (penX == 1 && penY == 1) {
+ drawLine(x0, y0, x1, y1, color, plotProc, data);
return;
-
- int thickX = (int)((float)thickness * dy / d / 2);
- int thickY = (int)((float)thickness * dx / d / 2);
-
- const int delta_x = ABS(x1 - x0);
- const int delta_y = ABS(y1 - y0);
- const int delta_err = delta_y;
- int x = x0;
- int y = y0;
- int err = 0;
-
- const int x_step = (x0 < x1) ? 1 : -1;
- const int y_step = (y0 < y1) ? 1 : -1;
-
- if (steep)
- drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data);
- else
- drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data);
-
- while (x != x1) {
- x += x_step;
- err += delta_err;
- if (2 * err > delta_x) {
- y += y_step;
- err -= delta_x;
- }
- if (steep)
- drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data);
- else
- drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data);
}
+
+ // TODO: Optimize this. It currently is a very naive way of handling
+ // thick lines since quite often it will be drawing to the same pixel
+ // multiple times.
+ for (int x = 0; x < penX; x++)
+ for (int y = 0; y < penY; y++)
+ drawLine(x0 + x, y0 + y, x1 + x, y1 + y, color, plotProc, data);
}
} // End of namespace Graphics
diff --git a/graphics/primitives.h b/graphics/primitives.h
index 0ab2dabcd8..f0780afc2e 100644
--- a/graphics/primitives.h
+++ b/graphics/primitives.h
@@ -25,7 +25,7 @@
namespace Graphics {
void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, int, int, void *), void *data);
-void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data);
+void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data);
} // End of namespace Graphics
diff --git a/graphics/scaler/aspect.cpp b/graphics/scaler/aspect.cpp
index f0ae732a40..327e7c5e89 100644
--- a/graphics/scaler/aspect.cpp
+++ b/graphics/scaler/aspect.cpp
@@ -23,6 +23,13 @@
#include "graphics/scaler/intern.h"
#include "graphics/scaler/aspect.h"
+#ifdef OPENPANDORA
+#define NEON_ASPECT_CORRECTOR
+#endif
+
+#ifdef NEON_ASPECT_CORRECTOR
+#include <arm_neon.h>
+#endif
#define kSuperFastAndUglyAspectMode 0 // No interpolation at all, but super-fast
#define kVeryFastAndGoodAspectMode 1 // Good quality with very good speed
@@ -55,13 +62,66 @@ static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint1
#if ASPECT_MODE == kVeryFastAndGoodAspectMode
+#ifdef NEON_ASPECT_CORRECTOR
+
+template<typename ColorMask>
+static void interpolate5LineNeon(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width, int k1, int k2) {
+ uint16x4_t kRedBlueMask_4 = vdup_n_u16(ColorMask::kRedBlueMask);
+ uint16x4_t kGreenMask_4 = vdup_n_u16(ColorMask::kGreenMask);
+ uint16x4_t k1_4 = vdup_n_u16(k1);
+ uint16x4_t k2_4 = vdup_n_u16(k2);
+ while (width >= 4) {
+ uint16x4_t srcA_4 = vld1_u16(srcA);
+ uint16x4_t srcB_4 = vld1_u16(srcB);
+ uint16x4_t p1_4 = srcB_4;
+ uint16x4_t p2_4 = srcA_4;
+
+ uint16x4_t p1_rb_4 = vand_u16(p1_4, kRedBlueMask_4);
+ uint16x4_t p1_g_4 = vand_u16(p1_4, kGreenMask_4);
+ uint16x4_t p2_rb_4 = vand_u16(p2_4, kRedBlueMask_4);
+ uint16x4_t p2_g_4 = vand_u16(p2_4, kGreenMask_4);
+
+ uint32x4_t tmp_rb_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_rb_4, k2_4), p1_rb_4, k1_4), 3);
+ uint32x4_t tmp_g_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_g_4, k2_4), p1_g_4, k1_4), 3);
+ uint16x4_t p_rb_4 = vmovn_u32(tmp_rb_4);
+ p_rb_4 = vand_u16(p_rb_4, kRedBlueMask_4);
+ uint16x4_t p_g_4 = vmovn_u32(tmp_g_4);
+ p_g_4 = vand_u16(p_g_4, kGreenMask_4);
+
+ uint16x4_t result_4 = p_rb_4 | p_g_4;
+ vst1_u16(dst, result_4);
+
+ dst += 4;
+ srcA += 4;
+ srcB += 4;
+ width -= 4;
+ }
+}
+#endif
+
template<typename ColorMask, int scale>
-static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) {
+static void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) {
if (scale == 1) {
+#ifdef NEON_ASPECT_CORRECTOR
+ int width4 = width & ~3;
+ interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 7, 1);
+ srcA += width4;
+ srcB += width4;
+ dst += width4;
+ width -= width4;
+#endif
while (width--) {
*dst++ = interpolate16_7_1<ColorMask>(*srcB++, *srcA++);
}
} else {
+#ifdef NEON_ASPECT_CORRECTOR
+ int width4 = width & ~3;
+ interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 5, 3);
+ srcA += width4;
+ srcB += width4;
+ dst += width4;
+ width -= width4;
+#endif
while (width--) {
*dst++ = interpolate16_5_3<ColorMask>(*srcB++, *srcA++);
}
diff --git a/graphics/sjis.h b/graphics/sjis.h
index 2d05005fc3..928332f712 100644
--- a/graphics/sjis.h
+++ b/graphics/sjis.h
@@ -169,7 +169,7 @@ protected:
bool _flippedMode;
int _fontWidth, _fontHeight;
uint8 _bitPosNewLineMask;
-
+
bool isASCII(uint16 ch) const;
virtual const uint8 *getCharData(uint16 c) const = 0;
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 1604eaa488..41ae8dcebb 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -50,6 +50,17 @@ void Surface::drawLine(int x0, int y0, int x1, int y1, uint32 color) {
error("Surface::drawLine: bytesPerPixel must be 1, 2, or 4");
}
+void Surface::drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) {
+ if (format.bytesPerPixel == 1)
+ Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<byte>, this);
+ else if (format.bytesPerPixel == 2)
+ Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint16>, this);
+ else if (format.bytesPerPixel == 4)
+ Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint32>, this);
+ else
+ error("Surface::drawThickLine: bytesPerPixel must be 1, 2, or 4");
+}
+
void Surface::create(uint16 width, uint16 height, const PixelFormat &f) {
free();
diff --git a/graphics/surface.h b/graphics/surface.h
index eaf62f84eb..6c9e464657 100644
--- a/graphics/surface.h
+++ b/graphics/surface.h
@@ -167,10 +167,26 @@ struct Surface {
* @param x1 The x coordinate of the end point.
* @param y1 The y coordinate of the end point.
* @param color The color of the line.
+ * @note This is just a wrapper around Graphics::drawLine
*/
void drawLine(int x0, int y0, int x1, int y1, uint32 color);
/**
+ * Draw a thick line.
+ *
+ * @param x0 The x coordinate of the start point.
+ * @param y0 The y coordiante of the start point.
+ * @param x1 The x coordinate of the end point.
+ * @param y1 The y coordinate of the end point.
+ * @param penX The width of the pen (thickness in the x direction)
+ * @param penY The height of the pen (thickness in the y direction)
+ * @param color The color of the line.
+ * @note This is just a wrapper around Graphics::drawThickLine
+ * @note The x/y coordinates of the start and end points are the upper-left most part of the pen
+ */
+ void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color);
+
+ /**
* Draw a horizontal line.
*
* @param x The start x coordinate of the line.
diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp
index 78903d0cd8..6043315a13 100644
--- a/graphics/yuv_to_rgb.cpp
+++ b/graphics/yuv_to_rgb.cpp
@@ -83,129 +83,127 @@
// BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-#include "common/scummsys.h"
-#include "common/singleton.h"
-
#include "graphics/surface.h"
+#include "graphics/yuv_to_rgb.h"
+
+namespace Common {
+DECLARE_SINGLETON(Graphics::YUVToRGBManager);
+}
namespace Graphics {
class YUVToRGBLookup {
public:
- YUVToRGBLookup(Graphics::PixelFormat format);
- ~YUVToRGBLookup();
-
- int16 *_colorTab;
- uint32 *_rgbToPix;
-};
+ YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale);
-YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format) {
- _colorTab = new int16[4 * 256]; // 2048 bytes
+ Graphics::PixelFormat getFormat() const { return _format; }
+ YUVToRGBManager::LuminanceScale getScale() const { return _scale; }
+ const uint32 *getRGBToPix() const { return _rgbToPix; }
- int16 *Cr_r_tab = &_colorTab[0 * 256];
- int16 *Cr_g_tab = &_colorTab[1 * 256];
- int16 *Cb_g_tab = &_colorTab[2 * 256];
- int16 *Cb_b_tab = &_colorTab[3 * 256];
+private:
+ Graphics::PixelFormat _format;
+ YUVToRGBManager::LuminanceScale _scale;
+ uint32 _rgbToPix[3 * 768]; // 9216 bytes
+};
- _rgbToPix = new uint32[3 * 768]; // 9216 bytes
+YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) {
+ _format = format;
+ _scale = scale;
uint32 *r_2_pix_alloc = &_rgbToPix[0 * 768];
uint32 *g_2_pix_alloc = &_rgbToPix[1 * 768];
uint32 *b_2_pix_alloc = &_rgbToPix[2 * 768];
- int16 CR, CB;
- int i;
+ if (scale == YUVToRGBManager::kScaleFull) {
+ // Set up entries 0-255 in rgb-to-pixel value tables.
+ for (int i = 0; i < 256; i++) {
+ r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0);
+ g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0);
+ b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i);
+ }
+
+ // Spread out the values we have to the rest of the array so that we do
+ // not need to check for overflow.
+ for (int i = 0; i < 256; i++) {
+ r_2_pix_alloc[i] = r_2_pix_alloc[256];
+ r_2_pix_alloc[i + 512] = r_2_pix_alloc[511];
+ g_2_pix_alloc[i] = g_2_pix_alloc[256];
+ g_2_pix_alloc[i + 512] = g_2_pix_alloc[511];
+ b_2_pix_alloc[i] = b_2_pix_alloc[256];
+ b_2_pix_alloc[i + 512] = b_2_pix_alloc[511];
+ }
+ } else {
+ // Set up entries 16-235 in rgb-to-pixel value tables
+ for (int i = 16; i < 236; i++) {
+ int scaledValue = (i - 16) * 255 / 219;
+ r_2_pix_alloc[i + 256] = format.RGBToColor(scaledValue, 0, 0);
+ g_2_pix_alloc[i + 256] = format.RGBToColor(0, scaledValue, 0);
+ b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, scaledValue);
+ }
+
+ // Spread out the values we have to the rest of the array so that we do
+ // not need to check for overflow. We have to do it here in two steps.
+ for (int i = 0; i < 256 + 16; i++) {
+ r_2_pix_alloc[i] = r_2_pix_alloc[256 + 16];
+ g_2_pix_alloc[i] = g_2_pix_alloc[256 + 16];
+ b_2_pix_alloc[i] = b_2_pix_alloc[256 + 16];
+ }
+
+ for (int i = 256 + 236; i < 768; i++) {
+ r_2_pix_alloc[i] = r_2_pix_alloc[256 + 236 - 1];
+ g_2_pix_alloc[i] = g_2_pix_alloc[256 + 236 - 1];
+ b_2_pix_alloc[i] = b_2_pix_alloc[256 + 236 - 1];
+ }
+ }
+}
+
+YUVToRGBManager::YUVToRGBManager() {
+ _lookup = 0;
+
+ int16 *Cr_r_tab = &_colorTab[0 * 256];
+ int16 *Cr_g_tab = &_colorTab[1 * 256];
+ int16 *Cb_g_tab = &_colorTab[2 * 256];
+ int16 *Cb_b_tab = &_colorTab[3 * 256];
// Generate the tables for the display surface
- for (i = 0; i < 256; i++) {
+ for (int i = 0; i < 256; i++) {
// Gamma correction (luminescence table) and chroma correction
// would be done here. See the Berkeley mpeg_play sources.
- CR = CB = (i - 128);
+ int16 CR = (i - 128), CB = CR;
Cr_r_tab[i] = (int16) ( (0.419 / 0.299) * CR) + 0 * 768 + 256;
Cr_g_tab[i] = (int16) (-(0.299 / 0.419) * CR) + 1 * 768 + 256;
Cb_g_tab[i] = (int16) (-(0.114 / 0.331) * CB);
Cb_b_tab[i] = (int16) ( (0.587 / 0.331) * CB) + 2 * 768 + 256;
}
-
- // Set up entries 0-255 in rgb-to-pixel value tables.
- for (i = 0; i < 256; i++) {
- r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0);
- g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0);
- b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i);
- }
-
- // Spread out the values we have to the rest of the array so that we do
- // not need to check for overflow.
- for (i = 0; i < 256; i++) {
- r_2_pix_alloc[i] = r_2_pix_alloc[256];
- r_2_pix_alloc[i + 512] = r_2_pix_alloc[511];
- g_2_pix_alloc[i] = g_2_pix_alloc[256];
- g_2_pix_alloc[i + 512] = g_2_pix_alloc[511];
- b_2_pix_alloc[i] = b_2_pix_alloc[256];
- b_2_pix_alloc[i + 512] = b_2_pix_alloc[511];
- }
-}
-
-YUVToRGBLookup::~YUVToRGBLookup() {
- delete[] _rgbToPix;
- delete[] _colorTab;
-}
-
-class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> {
-public:
- const YUVToRGBLookup *getLookup(Graphics::PixelFormat format);
-
-private:
- friend class Common::Singleton<SingletonBaseType>;
- YUVToRGBManager();
- ~YUVToRGBManager();
-
- Graphics::PixelFormat _lastFormat;
- YUVToRGBLookup *_lookup;
-};
-
-YUVToRGBManager::YUVToRGBManager() {
- _lookup = 0;
}
YUVToRGBManager::~YUVToRGBManager() {
delete _lookup;
}
-const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format) {
- if (_lastFormat == format)
+const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) {
+ if (_lookup && _lookup->getFormat() == format && _lookup->getScale() == scale)
return _lookup;
delete _lookup;
- _lookup = new YUVToRGBLookup(format);
- _lastFormat = format;
+ _lookup = new YUVToRGBLookup(format, scale);
return _lookup;
}
-} // End of namespace Graphics
-
-namespace Common {
-DECLARE_SINGLETON(Graphics::YUVToRGBManager);
-}
-
-#define YUVToRGBMan (Graphics::YUVToRGBManager::instance())
-
-namespace Graphics {
-
#define PUT_PIXEL(s, d) \
L = &rgbToPix[(s)]; \
*((PixelInt *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b])
template<typename PixelInt>
-void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Keep the tables in pointers here to avoid a dereference on each pixel
- const int16 *Cr_r_tab = lookup->_colorTab;
+ const int16 *Cr_r_tab = colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
- const uint32 *rgbToPix = lookup->_rgbToPix;
+ const uint32 *rgbToPix = lookup->getRGBToPix();
for (int h = 0; h < yHeight; h++) {
for (int w = 0; w < yWidth; w++) {
@@ -229,32 +227,32 @@ void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
}
}
-void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void YUVToRGBManager::convert444(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
assert(ySrc && uSrc && vSrc);
- const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
+ const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
- convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
- convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
template<typename PixelInt>
-void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
int halfHeight = yHeight >> 1;
int halfWidth = yWidth >> 1;
// Keep the tables in pointers here to avoid a dereference on each pixel
- const int16 *Cr_r_tab = lookup->_colorTab;
+ const int16 *Cr_r_tab = colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
- const uint32 *rgbToPix = lookup->_rgbToPix;
+ const uint32 *rgbToPix = lookup->getRGBToPix();
for (int h = 0; h < halfHeight; h++) {
for (int w = 0; w < halfWidth; w++) {
@@ -283,7 +281,7 @@ void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
}
}
-void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void YUVToRGBManager::convert420(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
@@ -291,13 +289,13 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS
assert((yWidth & 1) == 0);
assert((yHeight & 1) == 0);
- const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
+ const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
- convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
- convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
#define READ_QUAD(ptr, prefix) \
@@ -325,13 +323,13 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS
xDiff++
template<typename PixelInt>
-void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Keep the tables in pointers here to avoid a dereference on each pixel
- const int16 *Cr_r_tab = lookup->_colorTab;
+ const int16 *Cr_r_tab = colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
- const uint32 *rgbToPix = lookup->_rgbToPix;
+ const uint32 *rgbToPix = lookup->getRGBToPix();
int quarterWidth = yWidth >> 2;
@@ -368,7 +366,7 @@ void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
#undef DO_INTERPOLATION
#undef DO_YUV410_PIXEL
-void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void YUVToRGBManager::convert410(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
@@ -376,13 +374,13 @@ void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS
assert((yWidth & 3) == 0);
assert((yHeight & 3) == 0);
- const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
+ const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
- convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
- convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
} // End of namespace Graphics
diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h
index 73a2c69d7d..f785422c5a 100644
--- a/graphics/yuv_to_rgb.h
+++ b/graphics/yuv_to_rgb.h
@@ -32,57 +32,85 @@
#define GRAPHICS_YUV_TO_RGB_H
#include "common/scummsys.h"
+#include "common/singleton.h"
#include "graphics/surface.h"
namespace Graphics {
-/**
- * Convert a YUV444 image to an RGB surface
- *
- * @param dst the destination surface
- * @param ySrc the source of the y component
- * @param uSrc the source of the u component
- * @param vSrc the source of the v component
- * @param yWidth the width of the y surface
- * @param yHeight the height of the y surface
- * @param yPitch the pitch of the y surface
- * @param uvPitch the pitch of the u and v surfaces
- */
-void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+class YUVToRGBLookup;
-/**
- * Convert a YUV420 image to an RGB surface
- *
- * @param dst the destination surface
- * @param ySrc the source of the y component
- * @param uSrc the source of the u component
- * @param vSrc the source of the v component
- * @param yWidth the width of the y surface (must be divisible by 2)
- * @param yHeight the height of the y surface (must be divisible by 2)
- * @param yPitch the pitch of the y surface
- * @param uvPitch the pitch of the u and v surfaces
- */
-void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> {
+public:
+ /** The scale of the luminance values */
+ enum LuminanceScale {
+ kScaleFull, /** Luminance values range from [0, 255] */
+ kScaleITU /** Luminance values range from [16, 235], the range from ITU-R BT.601 */
+ };
-/**
- * Convert a YUV410 image to an RGB surface
- *
- * Since the chroma has a very low resolution in 410, we perform bilinear scaling
- * on the two chroma planes to produce the image. The chroma planes must have
- * at least one extra row that can be read from in order to produce a proper
- * image (filled with 0x80). This is required in order to speed up this function.
- *
- * @param dst the destination surface
- * @param ySrc the source of the y component
- * @param uSrc the source of the u component
- * @param vSrc the source of the v component
- * @param yWidth the width of the y surface (must be divisible by 4)
- * @param yHeight the height of the y surface (must be divisible by 4)
- * @param yPitch the pitch of the y surface
- * @param uvPitch the pitch of the u and v surfaces
- */
-void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+ /**
+ * Convert a YUV444 image to an RGB surface
+ *
+ * @param dst the destination surface
+ * @param scale the scale of the luminance values
+ * @param ySrc the source of the y component
+ * @param uSrc the source of the u component
+ * @param vSrc the source of the v component
+ * @param yWidth the width of the y surface
+ * @param yHeight the height of the y surface
+ * @param yPitch the pitch of the y surface
+ * @param uvPitch the pitch of the u and v surfaces
+ */
+ void convert444(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+
+ /**
+ * Convert a YUV420 image to an RGB surface
+ *
+ * @param dst the destination surface
+ * @param scale the scale of the luminance values
+ * @param ySrc the source of the y component
+ * @param uSrc the source of the u component
+ * @param vSrc the source of the v component
+ * @param yWidth the width of the y surface (must be divisible by 2)
+ * @param yHeight the height of the y surface (must be divisible by 2)
+ * @param yPitch the pitch of the y surface
+ * @param uvPitch the pitch of the u and v surfaces
+ */
+ void convert420(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+
+ /**
+ * Convert a YUV410 image to an RGB surface
+ *
+ * Since the chroma has a very low resolution in 410, we perform bilinear scaling
+ * on the two chroma planes to produce the image. The chroma planes must have
+ * at least one extra row and one extra column that can be read from in order to
+ * produce a proper image. It is suggested that you fill these in with the previous
+ * row and column's data. This is required in order to speed up this function.
+ *
+ * @param dst the destination surface
+ * @param scale the scale of the luminance values
+ * @param ySrc the source of the y component
+ * @param uSrc the source of the u component
+ * @param vSrc the source of the v component
+ * @param yWidth the width of the y surface (must be divisible by 4)
+ * @param yHeight the height of the y surface (must be divisible by 4)
+ * @param yPitch the pitch of the y surface
+ * @param uvPitch the pitch of the u and v surfaces
+ */
+ void convert410(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+
+private:
+ friend class Common::Singleton<SingletonBaseType>;
+ YUVToRGBManager();
+ ~YUVToRGBManager();
+
+ const YUVToRGBLookup *getLookup(Graphics::PixelFormat format, LuminanceScale scale);
+
+ YUVToRGBLookup *_lookup;
+ int16 _colorTab[4 * 256]; // 2048 bytes
+};
} // End of namespace Graphics
+#define YUVToRGBMan (::Graphics::YUVToRGBManager::instance())
+
#endif
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index 9a85399ed1..8285aff7ca 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -548,11 +548,11 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
else
return parserError("'" + stepNode->values["fill"] + "' is not a valid fill mode for a shape.");
}
-
+
if (stepNode->values.contains("padding")) {
val = stepNode->values["padding"];
int pr, pt, pl, pb;
- if (parseIntegerKey(val, 4, &pl, &pt, &pr, &pb))
+ if (parseIntegerKey(val, 4, &pl, &pt, &pr, &pb))
drawstep->padding.left = pl,
drawstep->padding.top = pt,
drawstep->padding.right = pr,
diff --git a/gui/ThemeParser.h b/gui/ThemeParser.h
index 82f774b803..360e3da009 100644
--- a/gui/ThemeParser.h
+++ b/gui/ThemeParser.h
@@ -139,7 +139,7 @@ protected:
XML_PROP(height, false)
XML_PROP(xpos, false)
XML_PROP(ypos, false)
- XML_PROP(padding, false)
+ XML_PROP(padding, false)
XML_PROP(orientation, false)
XML_PROP(file, false)
KEY_END()
diff --git a/gui/credits.h b/gui/credits.h
index fdde2da821..37c5a7bd95 100644
--- a/gui/credits.h
+++ b/gui/credits.h
@@ -159,6 +159,9 @@ static const char *credits[] = {
"C1""Parallaction",
"C0""peres",
"",
+"C1""Pegasus",
+"C0""Matthew Hoops",
+"",
"C1""Queen",
"C0""David Eriksson",
"C2""(retired)",
@@ -234,6 +237,11 @@ static const char *credits[] = {
"C0""Filippos Karapetis",
"C0""Joost Peters",
"",
+"C1""Tony",
+"C0""Arnaud Boutonn\351",
+"C0""Paul Gilbert",
+"C0""Alyssa Milburn",
+"",
"C1""Toon",
"C0""Sylvain Dupont",
"",
@@ -249,6 +257,9 @@ static const char *credits[] = {
"C0""Gregory Montoir",
"C2""(retired)",
"",
+"C1""Wintermute",
+"C0""Einar Johan T. S\370m\345en",
+"",
"",
"C1""Backend Teams",
"C1""Android",
@@ -482,6 +493,7 @@ static const char *credits[] = {
"C1""German",
"C0""Simon Sawatzki",
"C0""Lothar Serra Mari",
+"C2""(retired)",
"",
"C1""Hungarian",
"C0""Alex Bevilacqua",
@@ -679,6 +691,8 @@ static const char *credits[] = {
"C2""For generously providing hosting for our buildbot, SVN repository, planet and doxygen sites as well as tons of HD space",
"C0""DOSBox Team",
"C2""For their awesome OPL2 and OPL3 emulator",
+"C0""Yusuke Kamiyamane",
+"C2""For contributing some GUI icons ",
"C0""Till Kresslein",
"C2""For design of modern ScummVM GUI",
"C0""Jezar",
@@ -695,8 +709,6 @@ static const char *credits[] = {
"C2""For additional work on the original MT-32 emulator",
"C0""James Woodcock",
"C2""Soundtrack enhancements",
-"C0""Some icons by Yusuke Kamiyamane",
-"C0""",
"C0""Tony Warriner and everyone at Revolution Software Ltd. for sharing with us the source of some of their brilliant games, allowing us to release Beneath a Steel Sky as freeware... and generally being supportive above and beyond the call of duty.",
"C0""",
"C0""John Passfield and Steve Stamatiadis for sharing the source of their classic title, Flight of the Amazon Queen and also being incredibly supportive.",
@@ -717,7 +729,11 @@ static const char *credits[] = {
"C0""",
"C0""Neil Dodwell and David Dew from Creative Reality for providing the source of Dreamweb and for their tremendous support.",
"C0""",
-"C0""Janusz Wisniewski and Miroslaw Liminowicz from Laboratorium Komputerowe Avalon for providing full source code for Soltys and letting us to redistribute the game.",
+"C0""Janusz Wisniewski and Miroslaw Liminowicz from Laboratorium Komputerowe Avalon for providing full source code for Soltys and letting us redistribute the game.",
+"C0""",
+"C0""Jan Nedoma for providing the sources to the Wintermute-engine, and for his support while porting the engine to ScummVM.",
+"C0""",
+"C0""Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for providing the source code of The Journeyman Project: Pegasus Prime.",
"C0""",
"",
};
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 26fafa5279..0f4867ced5 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -166,7 +166,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
} else {
warning("Plugin for target \"%s\" not found! Game specific settings might be missing", domain.c_str());
}
-
+
// GAME: Path to game data (r/o), extra data (r/o), and save data (r/w)
String gamePath(ConfMan.get("path", _domain));
String extraPath(ConfMan.get("extrapath", _domain));
diff --git a/gui/options.cpp b/gui/options.cpp
index 4fc37c93da..4868f1876d 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -992,7 +992,7 @@ void OptionsDialog::addEngineControls(GuiObject *boss, const Common::String &pre
ExtraGuiOptions::const_iterator iter;
for (iter = engineOptions.begin(); iter != engineOptions.end(); ++iter, ++i) {
Common::String id = Common::String::format("%d", i);
- _engineCheckboxes.push_back(new CheckboxWidget(boss,
+ _engineCheckboxes.push_back(new CheckboxWidget(boss,
prefix + "customOption" + id + "Checkbox", _(iter->label), _(iter->tooltip)));
}
}
diff --git a/gui/predictivedialog.cpp b/gui/predictivedialog.cpp
index b827d49416..ed18847a40 100644
--- a/gui/predictivedialog.cpp
+++ b/gui/predictivedialog.cpp
@@ -71,7 +71,7 @@ PredictiveDialog::PredictiveDialog() : Dialog("Predictive") {
_btns = (ButtonWidget **)calloc(1, sizeof(ButtonWidget *) * 16);
- _btns[kCancelAct] = new ButtonWidget(this, "Predictive.Cancel", _("Cancel") , 0, kCancelCmd);
+ _btns[kCancelAct] = new ButtonWidget(this, "Predictive.Cancel", _("Cancel") , 0, kCancelCmd);
_btns[kOkAct] = new ButtonWidget(this, "Predictive.OK", _("Ok") , 0, kOkCmd);
_btns[kBtn1Act] = new ButtonWidget(this, "Predictive.Button1", "1 `-.&" , 0, kBut1Cmd);
_btns[kBtn2Act] = new ButtonWidget(this, "Predictive.Button2", "2 abc" , 0, kBut2Cmd);
@@ -84,10 +84,10 @@ PredictiveDialog::PredictiveDialog() : Dialog("Predictive") {
_btns[kBtn9Act] = new ButtonWidget(this, "Predictive.Button9", "9 wxyz" , 0, kBut9Cmd);
_btns[kBtn0Act] = new ButtonWidget(this, "Predictive.Button0", "0" , 0, kBut0Cmd);
// I18N: You must leave "#" as is, only word 'next' is translatable
- _btns[kNextAct] = new ButtonWidget(this, "Predictive.Next", _("# next") , 0, kNextCmd);
+ _btns[kNextAct] = new ButtonWidget(this, "Predictive.Next", _("# next") , 0, kNextCmd);
_btns[kAddAct] = new ButtonWidget(this, "Predictive.Add", _("add") , 0, kAddCmd);
_btns[kAddAct]->setEnabled(false);
-
+
#ifndef DISABLE_FANCY_THEMES
_btns[kDelAct] = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd);
((PicButtonWidget *)_btns[kDelAct])->useThemeTransparency(true);
@@ -214,7 +214,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_navigationwithkeys = true;
if (_lastbutton == kBtn1Act || _lastbutton == kBtn4Act || _lastbutton == kBtn7Act)
_currBtn = ButtonId(_lastbutton + 2);
- else if (_lastbutton == kDelAct)
+ else if (_lastbutton == kDelAct)
_currBtn = kBtn1Act;
else if (_lastbutton == kModeAct)
_currBtn = kNextAct;
@@ -227,7 +227,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
else
_currBtn = ButtonId(_lastbutton - 1);
-
+
if (_mode != kModeAbc && _lastbutton == kCancelAct)
_currBtn = kOkAct;
@@ -237,7 +237,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_navigationwithkeys = true;
if (_lastbutton == kBtn3Act || _lastbutton == kBtn6Act || _lastbutton == kBtn9Act || _lastbutton == kOkAct)
_currBtn = ButtonId(_lastbutton - 2);
- else if (_lastbutton == kDelAct)
+ else if (_lastbutton == kDelAct)
_currBtn = kBtn3Act;
else if (_lastbutton == kBtn0Act)
_currBtn = kNextAct;
@@ -249,7 +249,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_currBtn = kAddAct;
else
_currBtn = ButtonId(_lastbutton + 1);
-
+
if (_mode != kModeAbc && _lastbutton == kOkAct)
_currBtn = kCancelAct;
_needRefresh = true;
@@ -260,7 +260,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_currBtn = kDelAct;
else if (_lastbutton == kDelAct)
_currBtn = kOkAct;
- else if (_lastbutton == kModeAct)
+ else if (_lastbutton == kModeAct)
_currBtn = kBtn7Act;
else if (_lastbutton == kBtn0Act)
_currBtn = kBtn8Act;
@@ -286,7 +286,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_currBtn = kBtn0Act;
else if (_lastbutton == kBtn9Act)
_currBtn = kNextAct;
- else if (_lastbutton == kModeAct)
+ else if (_lastbutton == kModeAct)
_currBtn = kAddAct;
else if (_lastbutton == kBtn0Act)
_currBtn = kCancelAct;
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 850dfcc78f..df8dda7470 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -422,7 +422,28 @@ void SaveLoadChooserSimple::updateSelection(bool redraw) {
}
}
+void SaveLoadChooserSimple::open() {
+ SaveLoadChooserDialog::open();
+
+ // Scroll the list to the last used entry.
+ _list->scrollTo(ConfMan.getInt("gui_saveload_last_pos"));
+}
+
void SaveLoadChooserSimple::close() {
+ // Save the current scroll position/used entry.
+ const int result = getResult();
+ if (result >= 0) {
+ ConfMan.setInt("gui_saveload_last_pos", result);
+ } else {
+ // Use the current scroll position here.
+ // TODO: This means we canceled the dialog (or switch to the grid). Do
+ // we want to save this position here? Does the user want that?
+ // TODO: Do we want to save the current scroll position or the
+ // currently selected item here? The scroll position is what the user
+ // currently sees and seems to make more sense.
+ ConfMan.setInt("gui_saveload_last_pos", _list->getCurrentScrollPos());
+ }
+
_metaEngine = 0;
_target.clear();
_saveList.clear();
@@ -590,10 +611,29 @@ void SaveLoadChooserGrid::handleMouseWheel(int x, int y, int direction) {
void SaveLoadChooserGrid::open() {
SaveLoadChooserDialog::open();
- _curPage = 0;
_saveList = _metaEngine->listSaves(_target.c_str());
_resultString.clear();
+ // Load information to restore the last page the user had open.
+ assert(_entriesPerPage != 0);
+ const uint lastPos = ConfMan.getInt("gui_saveload_last_pos");
+ const uint listSize = _saveList.size();
+ uint bestMatch = 0;
+ uint diff = 0xFFFFFFFF;
+
+ // We look for the nearest available slot, since a slot might be missing
+ // due to the user deleting it via the list based chooser, by deleting
+ // it by hand, etc.
+ for (uint i = 0; i < listSize; ++i) {
+ uint curDiff = ABS(_saveList[i].getSaveSlot() - (int)lastPos);
+ if (curDiff < diff) {
+ diff = curDiff;
+ bestMatch = i;
+ }
+ }
+
+ _curPage = bestMatch / _entriesPerPage;
+
// Determine the next free save slot for save mode
if (_saveMode) {
int lastSlot = -1;
@@ -703,7 +743,7 @@ void SaveLoadChooserGrid::reflowLayout() {
if (!_saveMode) {
buttonCmd += 1;
}
-
+
PicButtonWidget *button = new PicButtonWidget(container, dstX, dstY, buttonWidth, buttonHeight, 0, buttonCmd);
dstY += buttonHeight;
@@ -718,6 +758,24 @@ void SaveLoadChooserGrid::reflowLayout() {
}
void SaveLoadChooserGrid::close() {
+ // Save the current page.
+ const int result = getResult();
+ if (result >= 0 && result != _nextFreeSaveSlot) {
+ // If the user selected a slot we use that one. We ignore new slots
+ // here, since otherwise the dialog would reset to page 0 when the
+ // user cancels the savename dialog.
+ ConfMan.setInt("gui_saveload_last_pos", result);
+ } else {
+ // Otherwise save the first entry on the current page.
+ // This is less precise than the solution above, since the number of
+ // entries shown differs between save and load version of the dialog,
+ // thus it might wrap to a different page than expected.
+ // Similar things happen on resolution changes.
+ // TODO: Should we ignore this here? Is the user likely to be
+ // interested in having this page restored when he canceled?
+ ConfMan.setInt("gui_saveload_last_pos", !_saveList.empty() ? _saveList[_curPage * _entriesPerPage].getSaveSlot() : 0);
+ }
+
SaveLoadChooserDialog::close();
hideButtons();
}
@@ -737,6 +795,12 @@ int SaveLoadChooserGrid::runIntern() {
slot = runModal();
} while (_saveMode && slot >= 0 && !selectDescription());
+ // Special case for new save games. We need to handle this here, since
+ // we cannot handle it in close() without problems.
+ if (slot == _nextFreeSaveSlot) {
+ ConfMan.setInt("gui_saveload_last_pos", slot);
+ }
+
return slot;
}
@@ -826,7 +890,7 @@ void SaveLoadChooserGrid::updateSaves() {
}
}
- const uint numPages = (_entriesPerPage != 0) ? (_saveList.size() / _entriesPerPage + 1) : 1;
+ const uint numPages = (_entriesPerPage != 0 && !_saveList.empty()) ? ((_saveList.size() + _entriesPerPage - 1) / _entriesPerPage) : 1;
_pageDisplay->setLabel(Common::String::format("%u/%u", _curPage + 1, numPages));
if (_curPage > 0)
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 9d0350d69d..6f7d95f73f 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -103,6 +103,7 @@ public:
virtual SaveLoadChooserType getType() const { return kSaveLoadDialogList; }
#endif // !DISABLE_SAVELOADCHOOSER_GRID
+ virtual void open();
virtual void close();
private:
virtual int runIntern();
diff --git a/gui/widget.cpp b/gui/widget.cpp
index c5ca628576..4ffb63e945 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -366,7 +366,7 @@ void ButtonWidget::startAnimatePressedState() {
}
void ButtonWidget::wantTickle(bool tickled) {
- if (tickled)
+ if (tickled)
((GUI::Dialog *)_boss)->setTickleWidget(this);
else
((GUI::Dialog *)_boss)->unSetTickleWidget();
diff --git a/gui/widgets/editable.h b/gui/widgets/editable.h
index 7e453c1204..4a18d5e689 100644
--- a/gui/widgets/editable.h
+++ b/gui/widgets/editable.h
@@ -77,10 +77,10 @@ public:
protected:
virtual void startEditMode() = 0;
virtual void endEditMode() = 0;
- virtual void abortEditMode() = 0;
+ virtual void abortEditMode() = 0;
virtual Common::Rect getEditRect() const = 0;
virtual int getCaretOffset() const;
- void drawCaret(bool erase);
+ void drawCaret(bool erase);
bool adjustOffset();
void makeCaretVisible();
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index 41fae37a71..47613b79f3 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -105,6 +105,7 @@ public:
void scrollTo(int item);
void scrollToEnd();
+ int getCurrentScrollPos() const { return _currentPos; }
void enableQuickSelect(bool enable) { _quickSelect = enable; }
String getQuickSelectString() const { return _quickSelectStr; }
diff --git a/ports.mk b/ports.mk
index ed6781a1a9..4d7d6f6e60 100644
--- a/ports.mk
+++ b/ports.mk
@@ -224,9 +224,6 @@ win32dist: $(EXECUTABLE)
mkdir -p $(WIN32PATH)/doc/se
$(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE)
cp $(DIST_FILES_THEMES) $(WIN32PATH)
-ifdef DIST_FILES_ENGINEDATA
- cp $(DIST_FILES_ENGINEDATA) $(WIN32PATH)
-endif
cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt
cp $(srcdir)/COPYING $(WIN32PATH)/COPYING.txt
cp $(srcdir)/COPYING.BSD $(WIN32PATH)/COPYING.BSD.txt
diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp
index 0d51f5b130..6062049b72 100644
--- a/video/avi_decoder.cpp
+++ b/video/avi_decoder.cpp
@@ -379,7 +379,7 @@ void AVIDecoder::readNextPacket() {
}
}
-AVIDecoder::AVIVideoTrack::AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader)
+AVIDecoder::AVIVideoTrack::AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader)
: _frameCount(frameCount), _vidsHeader(streamHeader), _bmInfo(bitmapInfoHeader) {
memset(_palette, 0, sizeof(_palette));
_videoCodec = createCodec();
diff --git a/video/avi_decoder.h b/video/avi_decoder.h
index a3a262db36..3bdc0561d1 100644
--- a/video/avi_decoder.h
+++ b/video/avi_decoder.h
@@ -214,7 +214,7 @@ private:
};
OldIndex _ixInfo;
- AVIHeader _header;
+ AVIHeader _header;
Common::SeekableReadStream *_fileStream;
bool _decodedHeader;
diff --git a/video/bink_decoder.cpp b/video/bink_decoder.cpp
index 620316806f..1ece22c963 100644
--- a/video/bink_decoder.cpp
+++ b/video/bink_decoder.cpp
@@ -236,7 +236,7 @@ BinkDecoder::AudioInfo::~AudioInfo() {
BinkDecoder::BinkVideoTrack::BinkVideoTrack(uint32 width, uint32 height, const Graphics::PixelFormat &format, uint32 frameCount, const Common::Rational &frameRate, bool swapPlanes, bool hasAlpha, uint32 id) :
_frameCount(frameCount), _frameRate(frameRate), _swapPlanes(swapPlanes), _hasAlpha(hasAlpha), _id(id) {
- _curFrame = -1;
+ _curFrame = -1;
for (int i = 0; i < 16; i++)
_huffman[i] = 0;
@@ -260,7 +260,23 @@ BinkDecoder::BinkVideoTrack::BinkVideoTrack(uint32 width, uint32 height, const G
_colHighHuffman[i].symbols[j] = j;
}
- _surface.create(width, height, format);
+ // Make the surface even-sized:
+ _surfaceHeight = height;
+ _surfaceWidth = width;
+
+ if (height & 1) {
+ _surfaceHeight++;
+ }
+ if (width & 1) {
+ _surfaceWidth++;
+ }
+
+ _surface.create(_surfaceWidth, _surfaceHeight, format);
+ // Since we over-allocate to make surfaces even-sized
+ // we need to set the actual VIDEO size back into the
+ // surface.
+ _surface.h = height;
+ _surface.w = width;
// Give the planes a bit extra space
width = _surface.w + 32;
@@ -329,9 +345,11 @@ void BinkDecoder::BinkVideoTrack::decodePacket(VideoFrame &frame) {
// Convert the YUV data we have to our format
// We're ignoring alpha for now
+ // The width used here is the surface-width, and not the video-width
+ // to allow for odd-sized videos.
assert(_curPlanes[0] && _curPlanes[1] && _curPlanes[2]);
- Graphics::convertYUV420ToRGB(&_surface, _curPlanes[0], _curPlanes[1], _curPlanes[2],
- _surface.w, _surface.h, _surface.w, _surface.w >> 1);
+ YUVToRGBMan.convert420(&_surface, Graphics::YUVToRGBManager::kScaleITU, _curPlanes[0], _curPlanes[1], _curPlanes[2],
+ _surfaceWidth, _surfaceHeight, _surfaceWidth, _surfaceWidth >> 1);
// And swap the planes with the reference planes
for (int i = 0; i < 4; i++)
diff --git a/video/bink_decoder.h b/video/bink_decoder.h
index 150e91aab7..27d3aa3691 100644
--- a/video/bink_decoder.h
+++ b/video/bink_decoder.h
@@ -231,6 +231,8 @@ private:
int _frameCount;
Graphics::Surface _surface;
+ int _surfaceWidth; ///< The actual surface width
+ int _surfaceHeight; ///< The actual surface height
uint32 _id; ///< The BIK FourCC.
diff --git a/video/codecs/svq1.cpp b/video/codecs/svq1.cpp
index 14452ab15b..56b376f590 100644
--- a/video/codecs/svq1.cpp
+++ b/video/codecs/svq1.cpp
@@ -180,28 +180,29 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st
frameData.skip(8);
}
- int yWidth = ALIGN(_frameWidth, 16);
- int yHeight = ALIGN(_frameHeight, 16);
- int uvWidth = ALIGN(yWidth / 4, 16);
- int uvHeight = ALIGN(yHeight / 4, 16);
+ uint yWidth = ALIGN(_frameWidth, 16);
+ uint yHeight = ALIGN(_frameHeight, 16);
+ uint uvWidth = ALIGN(yWidth / 4, 16);
+ uint uvHeight = ALIGN(yHeight / 4, 16);
+ uint uvPitch = uvWidth + 4; // we need at least one extra column and pitch must be divisible by 4
byte *current[3];
// Decode Y, U and V component planes
for (int i = 0; i < 3; i++) {
- int width, height;
+ uint width, height, pitch;
if (i == 0) {
width = yWidth;
height = yHeight;
+ pitch = width;
current[i] = new byte[width * height];
} else {
width = uvWidth;
height = uvHeight;
+ pitch = uvPitch;
- // Add an extra row's worth of data to not go out-of-bounds in the
- // color conversion. Then fill that with "empty" data.
- current[i] = new byte[width * (height + 1)];
- memset(current[i] + width * height, 0x80, width);
+ // Add an extra row here. See below for more information.
+ current[i] = new byte[pitch * (height + 1)];
}
if (frameType == 0) { // I Frame
@@ -209,12 +210,12 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st
byte *currentP = current[i];
for (uint16 y = 0; y < height; y += 16) {
for (uint16 x = 0; x < width; x += 16) {
- if (!svq1DecodeBlockIntra(&frameData, &currentP[x], width)) {
+ if (!svq1DecodeBlockIntra(&frameData, &currentP[x], pitch)) {
warning("svq1DecodeBlockIntra decode failure");
return _surface;
}
}
- currentP += 16 * width;
+ currentP += 16 * pitch;
}
} else {
// Delta frame (P or B)
@@ -233,7 +234,7 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st
byte *currentP = current[i];
for (uint16 y = 0; y < height; y += 16) {
for (uint16 x = 0; x < width; x += 16) {
- if (!svq1DecodeDeltaBlock(&frameData, &currentP[x], previous, width, pmv, x, y)) {
+ if (!svq1DecodeDeltaBlock(&frameData, &currentP[x], previous, pitch, pmv, x, y)) {
warning("svq1DecodeDeltaBlock decode failure");
return _surface;
}
@@ -241,7 +242,7 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st
pmv[0].x = pmv[0].y = 0;
- currentP += 16 * width;
+ currentP += 16 * pitch;
}
delete[] pmv;
@@ -256,7 +257,21 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st
_surface->h = _height;
}
- convertYUV410ToRGB(_surface, current[0], current[1], current[2], yWidth, yHeight, yWidth, uvWidth);
+ // We need to massage the chrominance data a bit to be able to be used by the converter
+ // Since the thing peeks at values one column and one row beyond the data, we need to fill it in
+
+ // First, fill in the column-after-last with the last column's value
+ for (uint i = 0; i < uvHeight; i++) {
+ current[1][i * uvPitch + uvWidth] = current[1][i * uvPitch + uvWidth - 1];
+ current[2][i * uvPitch + uvWidth] = current[2][i * uvPitch + uvWidth - 1];
+ }
+
+ // Then, copy the last row to the one after the last row
+ memcpy(current[1] + uvHeight * uvPitch, current[1] + (uvHeight - 1) * uvPitch, uvWidth + 1);
+ memcpy(current[2] + uvHeight * uvPitch, current[2] + (uvHeight - 1) * uvPitch, uvWidth + 1);
+
+ // Finally, actually do the conversion ;)
+ YUVToRGBMan.convert410(_surface, Graphics::YUVToRGBManager::kScaleFull, current[0], current[1], current[2], yWidth, yHeight, yWidth, uvPitch);
// Store the current surfaces for later and free the old ones
for (int i = 0; i < 3; i++) {
diff --git a/video/coktel_decoder.cpp b/video/coktel_decoder.cpp
index 5d7efe87af..08340a19a6 100644
--- a/video/coktel_decoder.cpp
+++ b/video/coktel_decoder.cpp
@@ -1317,7 +1317,7 @@ void IMDDecoder::processFrame() {
// Set palette
if (cmd == kCommandPalette) {
_stream->skip(2);
-
+
_paletteDirty = true;
for (int i = 0; i < 768; i++)
diff --git a/video/psx_decoder.cpp b/video/psx_decoder.cpp
index fa7f1e8cfe..57c8972ee5 100644
--- a/video/psx_decoder.cpp
+++ b/video/psx_decoder.cpp
@@ -234,7 +234,7 @@ void PSXStreamDecoder::readNextPacket() {
Common::SeekableReadStream *frame = new Common::MemoryReadStream(partialFrame, frameSize, DisposeAfterUse::YES);
_videoTrack->decodeFrame(frame, sectorsRead);
-
+
delete frame;
delete sector;
return;
@@ -297,7 +297,7 @@ Common::SeekableReadStream *PSXStreamDecoder::readSector() {
// Ha! It's palindromic!
#define AUDIO_DATA_CHUNK_SIZE 2304
-#define AUDIO_DATA_SAMPLE_COUNT 4032
+#define AUDIO_DATA_SAMPLE_COUNT 4032
static const int s_xaTable[5][2] = {
{ 0, 0 },
@@ -483,7 +483,7 @@ void PSXStreamDecoder::PSXVideoTrack::decodeFrame(Common::SeekableReadStream *fr
decodeMacroBlock(&bits, mbX, mbY, scale, version);
// Output data onto the frame
- Graphics::convertYUV420ToRGB(_surface, _yBuffer, _cbBuffer, _crBuffer, _surface->w, _surface->h, _macroBlocksW * 16, _macroBlocksW * 8);
+ YUVToRGBMan.convert420(_surface, Graphics::YUVToRGBManager::kScaleFull, _yBuffer, _cbBuffer, _crBuffer, _surface->w, _surface->h, _macroBlocksW * 16, _macroBlocksW * 8);
_curFrame++;
diff --git a/video/psx_decoder.h b/video/psx_decoder.h
index 11f311594d..d1d5204e37 100644
--- a/video/psx_decoder.h
+++ b/video/psx_decoder.h
@@ -149,7 +149,7 @@ private:
uint32 _frameCount;
Common::SeekableReadStream *_stream;
PSXVideoTrack *_videoTrack;
- PSXAudioTrack *_audioTrack;
+ PSXAudioTrack *_audioTrack;
Common::SeekableReadStream *readSector();
};
diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp
index 87c530dba0..b4dab9ddfb 100644
--- a/video/qt_decoder.cpp
+++ b/video/qt_decoder.cpp
@@ -216,7 +216,7 @@ void QuickTimeDecoder::init() {
addTrack(new AudioTrackHandler(this, _audioTracks[i]));
// Initialize all the video tracks
- Common::Array<Common::QuickTimeParser::Track *> &tracks = Common::QuickTimeParser::_tracks;
+ const Common::Array<Common::QuickTimeParser::Track *> &tracks = Common::QuickTimeParser::_tracks;
for (uint32 i = 0; i < tracks.size(); i++) {
if (tracks[i]->codecType == CODEC_TYPE_VIDEO) {
for (uint32 j = 0; j < tracks[i]->sampleDescs.size(); j++)
diff --git a/video/qt_decoder.h b/video/qt_decoder.h
index 71d33711a6..45ab155c2c 100644
--- a/video/qt_decoder.h
+++ b/video/qt_decoder.h
@@ -53,6 +53,7 @@ class Codec;
*
* Video decoder used in engines:
* - mohawk
+ * - pegasus
* - sci
*/
class QuickTimeDecoder : public VideoDecoder, public Audio::QuickTimeAudioDecoder {
diff --git a/video/smk_decoder.cpp b/video/smk_decoder.cpp
index bea65142a1..c49791100d 100644
--- a/video/smk_decoder.cpp
+++ b/video/smk_decoder.cpp
@@ -318,7 +318,7 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
// 1 - set to 1 if file is Y-interlaced
// 2 - set to 1 if file is Y-doubled
// If bits 1 or 2 are set, the frame should be scaled to twice its height
- // before it is displayed.
+ // before it is displayed.
_header.flags = _fileStream->readUint32LE();
SmackerVideoTrack *videoTrack = createVideoTrack(width, height, frameCount, frameRate, _header.flags, _header.signature);
diff --git a/video/theora_decoder.cpp b/video/theora_decoder.cpp
index d7260469e6..63aa93e2f5 100644
--- a/video/theora_decoder.cpp
+++ b/video/theora_decoder.cpp
@@ -302,7 +302,7 @@ bool TheoraDecoder::TheoraVideoTrack::decodePacket(ogg_packet &oggPacket) {
_nextFrameStartTime += _frameRate.getInverse().toDouble();
else
_nextFrameStartTime = time;
-
+
return true;
}
@@ -328,7 +328,7 @@ void TheoraDecoder::TheoraVideoTrack::translateYUVtoRGBA(th_ycbcr_buffer &YUVBuf
assert(YUVBuffer[kBufferU].height == YUVBuffer[kBufferY].height >> 1);
assert(YUVBuffer[kBufferV].height == YUVBuffer[kBufferY].height >> 1);
- Graphics::convertYUV420ToRGB(&_surface, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride);
+ YUVToRGBMan.convert420(&_surface, Graphics::YUVToRGBManager::kScaleITU, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride);
}
static vorbis_info *info = 0;
diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp
index 559880acee..110afa7755 100644
--- a/video/video_decoder.cpp
+++ b/video/video_decoder.cpp
@@ -35,7 +35,6 @@ namespace Video {
VideoDecoder::VideoDecoder() {
_startTime = 0;
- _needsRewind = false;
_dirtyPalette = false;
_palette = 0;
_isPlaying = false;
@@ -62,7 +61,6 @@ void VideoDecoder::close() {
delete *it;
_tracks.clear();
- _needsRewind = false;
_dirtyPalette = false;
_palette = 0;
_startTime = 0;
@@ -87,7 +85,7 @@ bool VideoDecoder::loadFile(const Common::String &filename) {
}
bool VideoDecoder::needsUpdate() const {
- return !endOfVideo() && getTimeToNextFrame() == 0;
+ return hasFramesLeft() && getTimeToNextFrame() == 0;
}
void VideoDecoder::pauseVideo(bool pause) {
@@ -249,18 +247,8 @@ uint32 VideoDecoder::getTimeToNextFrame() const {
}
bool VideoDecoder::endOfVideo() const {
- if (!isVideoLoaded())
- return true;
-
- if (_endTimeSet) {
- const VideoTrack *track = findNextVideoTrack();
-
- if (track && track->getNextFrameStartTime() >= (uint)_endTime.msecs())
- return true;
- }
-
for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++)
- if (!(*it)->endOfTrack())
+ if (!(*it)->endOfTrack() && (!isPlaying() || (*it)->getTrackType() != Track::kTrackTypeVideo || !_endTimeSet || ((VideoTrack *)*it)->getNextFrameStartTime() < (uint)_endTime.msecs()))
return false;
return true;
@@ -281,8 +269,6 @@ bool VideoDecoder::rewind() {
if (!isRewindable())
return false;
- _needsRewind = false;
-
// Stop all tracks so they can be rewound
if (isPlaying())
stopAudio();
@@ -316,8 +302,6 @@ bool VideoDecoder::seek(const Audio::Timestamp &time) {
if (!isSeekable())
return false;
- _needsRewind = false;
-
// Stop all tracks so they can be seeked
if (isPlaying())
stopAudio();
@@ -347,10 +331,6 @@ void VideoDecoder::start() {
_isPlaying = true;
_startTime = g_system->getMillis();
- // If someone previously called stop(), we'll rewind it.
- if (_needsRewind)
- rewind();
-
// Adjust start time if we've seeked to something besides zero time
if (_lastTimeChange.totalNumberOfFrames() != 0)
_startTime -= _lastTimeChange.msecs();
@@ -362,26 +342,27 @@ void VideoDecoder::stop() {
if (!isPlaying())
return;
+ // Stop audio here so we don't have it affect getTime()
+ stopAudio();
+
+ // Keep the time marked down in case we start up again
+ // We do this before _isPlaying is set so we don't get
+ // _lastTimeChange returned, but before _pauseLevel is
+ // reset.
+ _lastTimeChange = getTime();
+
_isPlaying = false;
_startTime = 0;
_palette = 0;
_dirtyPalette = false;
_needsUpdate = false;
- stopAudio();
-
// Also reset the pause state.
_pauseLevel = 0;
- // If this is a rewindable video, don't close it too. We'll just rewind() the video
- // the next time someone calls start(). Otherwise, since it can't be rewound, we
- // just close it.
- if (isRewindable()) {
- _lastTimeChange = getTime();
- _needsRewind = true;
- } else {
- close();
- }
+ // Reset the pause state of the tracks too
+ for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++)
+ (*it)->pause(false);
}
Audio::Timestamp VideoDecoder::getDuration() const {
@@ -409,6 +390,11 @@ bool VideoDecoder::Track::rewind() {
return seek(Audio::Timestamp(0, 1000));
}
+void VideoDecoder::Track::pause(bool shouldPause) {
+ _paused = shouldPause;
+ pauseIntern(shouldPause);
+}
+
Audio::Timestamp VideoDecoder::Track::getDuration() const {
return Audio::Timestamp(0, 1000);
}
@@ -679,4 +665,15 @@ void VideoDecoder::startAudioLimit(const Audio::Timestamp &limit) {
((AudioTrack *)*it)->start(limit);
}
+bool VideoDecoder::hasFramesLeft() const {
+ // This is similar to endOfVideo(), except it doesn't take Audio into account (and returns true if not the end of the video)
+ // This is only used for needsUpdate() atm so that setEndTime() works properly
+ // And unlike endOfVideoTracks(), this takes into account _endTime
+ for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++)
+ if ((*it)->getTrackType() == Track::kTrackTypeVideo && !(*it)->endOfTrack() && (!isPlaying() || !_endTimeSet || ((VideoTrack *)*it)->getNextFrameStartTime() < (uint)_endTime.msecs()))
+ return true;
+
+ return false;
+}
+
} // End of namespace Video
diff --git a/video/video_decoder.h b/video/video_decoder.h
index 5abe1d917c..cc7d1df51b 100644
--- a/video/video_decoder.h
+++ b/video/video_decoder.h
@@ -102,16 +102,14 @@ public:
/**
* Begin playback of the video.
*
- * @note This has no effect is the video is already playing.
+ * @note This has no effect if the video is already playing.
*/
void start();
/**
* Stop playback of the video.
*
- * @note This will close() the video if it is not rewindable.
- * @note If the video is rewindable, the video will be rewound on the
- * next start() call unless rewind() or seek() is called before then.
+ * @note This has no effect if the video is not playing.
*/
void stop();
@@ -180,6 +178,9 @@ public:
/**
* Set the time for this video to end at. At this time in the video,
* all audio will stop and endOfVideo() will return true.
+ *
+ * While the setting is stored even if a video is not playing,
+ * endOfVideo() is only affected when the video is playing.
*/
void setEndTime(const Audio::Timestamp &endTime);
@@ -203,7 +204,7 @@ public:
* Returns the current frame number of the video.
* @return the last frame decoded by the video
*/
- int32 getCurFrame() const;
+ int getCurFrame() const;
/**
* Returns the number of frames in the video.
@@ -432,7 +433,7 @@ protected:
/**
* Set the pause status of the track.
*/
- void pause(bool shouldPause) {}
+ void pause(bool shouldPause);
/**
* Return if the track is paused.
@@ -450,7 +451,7 @@ protected:
/**
* Function called by pause() for subclasses to implement.
*/
- void pauseIntern(bool pause);
+ virtual void pauseIntern(bool shouldPause) {}
private:
bool _paused;
@@ -596,7 +597,7 @@ protected:
virtual Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kPlainSoundType; }
protected:
- void pauseIntern(bool pause);
+ void pauseIntern(bool shouldPause);
/**
* Get the AudioStream that is the representation of this AudioTrack
@@ -762,7 +763,7 @@ private:
TrackList _tracks;
// Current playback status
- bool _isPlaying, _needsRewind, _needsUpdate;
+ bool _isPlaying, _needsUpdate;
Audio::Timestamp _lastTimeChange, _endTime;
bool _endTimeSet;
@@ -777,6 +778,7 @@ private:
void stopAudio();
void startAudio();
void startAudioLimit(const Audio::Timestamp &limit);
+ bool hasFramesLeft() const;
int32 _startTime;
uint32 _pauseLevel;